线性筛选法求素数

本文详细介绍了素数判断的基本算法,包括普通求法、sqrt(n)改进法及线性筛法(欧拉筛法)。通过逐步优化,最终实现高效的素数筛选,并利用二分查找进行数据对比,提高算法性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

普通求法:

#include<stdio.h>
int main()
{
    int i, n;
    while (scanf("%d", &n) == 1)
    {
         for (i = 2;i <
n;i++)
             if (n % i == 0)
                  break;
         if (i == n)
             printf("Yes\n");
         else
             printf("No\n");
    }
    return 0;
}

这个缺点就是如果n稍微大点运行的时间就很大

在普通的基础上改进方法(sqrt(n)):

#include<stdio.h>
#include<math.h>
int main()
{
    int i, n, x;
    while (scanf("%d", &n) == 1)
    {
         x= (int)sqrt(n);
         for (i = 2;i <=x;i++)
             if (n % i == 0)
                  break;
         if (i > x)
             printf("Yes\n");
         else
             printf("NO\n");
    }
    return 0;
}

用的sqrt,效率提高了,但是还是不够理想

于是我们有线性筛法(欧拉筛法):

原理:素数的倍数一定不是素数。

我们可以用一个长度为n+1的数组来存储信息(这种用另一个数组来保存信息的方法很常见也很有用),首先将所有的数字全初始化为0(素数),再将数字1与第一个素数2标记为1(非素数)从素数2开始将所有小于n的2的倍数都标记为1;继续该过程,将素数3的倍数筛掉,到循环结束时,标记仍为0的数就是素数。

//primer用来保存得到的素数

memset(check , 0 , sizeof(check) );

check[1] = 1;

int sum = 0,i,j;

for(i = 2;i <= n; i++)

{

       if(!check[i])

              primer[sum++]
= i;

       for(j
= i+1;j <= n;j += i)

              check[i]
= 1;

}

在这里插入图片描述

仔细分析这个算法的优化已经很好了,但是有些数字被重复使用了

于是乎真正的欧拉筛选来了

欧拉筛选保证一个合数只会被他最小的质数因子筛序一次

代码如下



check[1] = 1;
int sum =0, i, j;
for (i =
2;i <= n;i++)
{
    if (!check[i])
         primer[sum++]= i;
    for (j = 0;j <sum;j++)
    {
         if (i * primer[j] >n)
             break;
         check[i* primer[j]] = 1;
         if (i % primer[j] ==0)
             break;
    }
}

在这里插入图片描述
在这里插入图片描述
思路:

1.用线性筛法求出N范围内的素数保存在primer中(友情提示:大数组最好放在main函数之外定义,其他的题最好也是这样。为什么?百度“栈帧”,你就知道)
2.对这M组数据,用二分查找对比primer。



#include <stdio.h>

int n,w, primer[1000010],tot,s,i;

int check[100000010];

int main()

{

   
scanf("%d%d",&n,&w);

   
for(i = 2; i <= n; i++)

    {

       
if(check[i] == 0) primer[++tot] = i;

       
for(int j = 1; (j <= tot) && (i*primer[j] <= n); j++)

       
{

           
check[i * primer[j]] = 1;

           
if(i % primer[j] == 0) break;

       
}

    }

   
while(w--)

    {

       
scanf("%d",&s);

       
i = 1;

       
int j = tot, mid;

       
while(i <= j)

       
{

           
mid = (i + j) / 2;

           
if(s == primer[mid])

           
{

                printf("Yes\n");

                break;

           
}

           
else if(s < primer[mid]) j = mid - 1;

           
else i = mid + 1;

       
}

       
if(s != primer[mid]) printf("No\n");

    }

   
return 0;

}

参考资料

线性筛法(也称为欧拉筛法)是一种高效的素数筛选算法,其时间复杂度为 $O(n)$,能够在线性时间内筛选出 $1$ 到 $n$ 之间的所有素数。该方法的核心思想是通过遍历每个数,并将合数仅被标记一次,从而避免重复筛除,提高效率。 以下是一个使用 C++ 实现线性筛法的示例代码: ```cpp #include <iostream> #include <vector> std::vector<int> linear_sieve(int n) { std::vector<bool> is_prime(n + 1, true); // 用于标记每个数是否为素数 std::vector<int> primes; // 存储素数列表 for (int i = 2; i <= n; ++i) { if (is_prime[i]) { primes.push_back(i); // 如果i是素数,加入素数列表 } // 遍历当前已找到的素数列表 for (size_t j = 0; j < primes.size() && i * primes[j] <= n; ++j) { is_prime[i * primes[j]] = false; // 标记该合数 if (i % primes[j] == 0) { break; // 确保每个合数只被其最小的质因数筛除一次 } } } return primes; // 返回n以内的所有素数 } int main() { int n; std::cout << "请输入一个正整数n:"; std::cin >> n; std::vector<int> primes = linear_sieve(n); std::cout << "1到" << n << "之间的素数有:" << std::endl; for (int prime : primes) { std::cout << prime << " "; } std::cout << std::endl; return 0; } ``` ### 代码说明: 1. **标记数组 `is_prime`**:用于记录每个数是否为素数,初始时设为 `true`。 2. **素数列表 `primes`**:存储筛选出的素数。 3. **外层循环**:从 $2$ 到 $n$ 遍历每个数 $i$。 4. **内层循环**:对于每个素数 $p$,标记 $i \times p$ 为合数;当 $i$ 能被当前素数整除时,停止内层循环以避免重复标记。 5. **输出结果**:将筛选出的素数打印出来。 ### 算法特点: - **高效性**:每个合数只会被其最小的质因数筛除一次,因此时间复杂度为 $O(n)$。 - **空间复杂度**:需要一个大小为 $n+1$ 的布尔数组来标记素数,因此空间复杂度为 $O(n)$。 通过该算法,可以在较短时间内处理大规模的素数筛选问题,适用于对性能要较高的场景。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值