线性筛法求素数

出处

http://www.cnblogs.com/grubbyskyer/p/3852421.html

普通筛选法–埃拉托斯特尼筛法

先简单说一下原理:

基本思想:素数的倍数一定不是素数
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
说明:整数1特殊处理即可。

举个例子,N=20时,演示如下图:
这里写图片描述

最后数组里面还是0的就是素数了…

代码实现如下:

prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,………} tot 是当前得到的素数的个数 check :0表示是素数 1表示合数

*#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int M = 2000;
int prime[M + 10];
int check[M];
int main()
{
    int t = 0;
    memset(check,0,sizeof(check));
    for(int i = 2; i <= M - 1; ++i)
    {
        if(!check[i])
        {
            prime[t++] = i;
            for(int j = 2; j * i<= M - 1;++j)
            {
                check[j * i] = 1;
            }
        }
    }
    cout << t << endl;
    for(int i = 0; i <= t - 1; ++i)
    {
        cout << prime[i] << ',';
    }
    return 0;
}

此筛选法的时间复杂度是O(nloglogn) 空间复杂度是O(n)

不足之处也比较明显,手动模拟一遍就会发现,很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又标记一次,因此又造成了比较大的不必要处理…那有没有改进的办法呢…就是下面改进之后的筛法…

线性筛法–欧拉筛法

#include<cstring>
#include<iostream>
using namespace std;
const int M = 2000;
int prime[M + 10];
void getprime()
{
    memset(prime,0,sizeof(prime));
    for(int i = 2; i <= M; ++i)
    {
        if(!prime[i])
        {
            prime[++prime[0]] = i;
        }
        for(int j = 1; j <= prime[0]  && i < M / prime[j]; ++j)
        {
            prime[i * prime[j]] = 1;
            if(i % prime[j] == 0) // 保证了每个数只被筛一次
                break;
        }
    }

}
int main()
{
    getprime();
    cout << prime[0] << endl;
    for(int i = 1; i <= prime[0] - 1; ++i)
    {
        cout << prime[i] << ',';
    }
    return 0;
}

精华就在于红色标注那两处,它们保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)
此过程中保证了两点:

1、合数一定被干掉了…

2、每个数都没有被重复地删掉

这里写图片描述

扩展博客:http://suno.cnblogs.com/

线性筛法(也称为欧拉筛法)是一种高效的素数筛选算法,其间复杂度为 $ O(n) $,适用于大规模数据的素数筛选。该算法的核心思想是通过遍历每个数,将合数逐一筛掉,同确保每个合数只被其最小的质因数筛除一,从而避免重复操作。 以下是使用 C 语言实现线性筛法素数的代码示例: ```c #include <stdio.h> #include <stdbool.h> #include <string.h> #define MAX 1000000 // 定义最大范围 int primes[MAX]; // 存储所有素数 bool st[MAX]; // 标记数组,记录是否已被筛除 void get_primes(int n) { int cnt = 0; // 记录素数个数 memset(st, 0, sizeof(st)); // 初始化标记数组 for (int i = 2; i <= n; i++) { if (!st[i]) { primes[cnt++] = i; // 如果未被标记,则i为素数 } for (int j = 0; j < cnt && primes[j] <= n / i; j++) { st[primes[j] * i] = true; // 标记primes[j] * i为合数 if (i % primes[j] == 0) { break; // 确保每个合数只被其最小的质因数筛除一 } } } // 输出所有素数 for (int i = 0; i < cnt; i++) { printf("%d ", primes[i]); } printf("\n"); } int main() { int n; printf("请输入一个整数N:"); scanf("%d", &n); get_primes(n); return 0; } ``` ### 代码说明: 1. **初始化标记数组 `st`**:使用 `memset` 函数将标记数组初始化为 `false`,表示所有数未被筛除。 2. **外层循环遍历 `i`**:从 2 开始遍历到 `n`,如果 `i` 未被标记,则将其加入素数数组 `primes` 中。 3. **内层循环筛除合数**:对于每个素数 `primes[j]`,将 `primes[j] * i` 标记合数;当 `i % primes[j] == 0` ,终止循环,确保每个合数只被其最小的质因数筛除一。 4. **输出素数数组**:最后输出所有筛选出的素数。 该算法的效率较高,适用于大规模数据的素数筛选,尤其适合在需要多次查询素数的情况下使用 [^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值