素数筛法及其优化

要求1~10^6范围内的素数,如果用自定义函数挨个求,对于大范围的求素数会非常耗时。复杂度为O(n * sqrt(n)),所以可以用素数筛法来求大范围内的素数
说一下比较暴力的思路:
开一个标记数组,全部初始化为true,0、1不是素数,直接标记为false。
从未遍历过的最小的标记为 true 的位置开始,所有是这个位置的下标的倍数的位置都标记为 false。
从一开始:
数组中下标最小的、标记为 true 的位置,下标是 2 ,从下标 2 开始,凡是 2 的倍数、且小于 10^6 的,全部标记为 false 。
再找下标 2 以后的、标记为 true 的下一个位置,下标是 3。从下标 3 开始,凡是 3 的倍数、且小于10^6 的,全部标记为false。
再找下标 3 以后的、标记为 true 的下一个位置,下标是 5。从下标 5 开始,凡是 5 的倍数、且小于 10^6 的,全部标记为 false 。
这样循环,直到开始的位置大于 100000 ,退出循环。
这样,所有标记为 TRUE 的元素的下标全是素数。
贴上代码

const size = 1000000; //  1e6
bool isprime[size + 10];
memset(isprime, true, sizeof(isprime));
isprime[0] = isprime[1] = false;
for (int i = 2; i <= size ; i++)
{
    if (isprime[i])
        for (long long j = 2; j * i <= size ; j++)
            isprime[j * i] = false;
}

然而这样做会有重复的标记为 false 的过程,想要提高效率,还可以采用下面这种办法,在对大数据量处理的时候会有明显的优势。
例如,对于更大的的数据量,可以用下面这段代码。
代码前面有一个预处理:所有大于 2 的偶数全都不是素数,标记为false。

const int size = 100000000 // 1e8
bool isprime[size + 10];
memset(isprime, true, sizeof(isprime));
isprime[0] = isprime[1] = false;
for (int i = 4; i <= size; i += 2) // 所有大于 2 的偶数全都不是素数,首先划掉
    isprime[i] = false;
for (long long i = 3; i <= size; i++) // 注意要用long long,否则会溢出
{
    if (isprime[i])
        for (long long j = i * i; j <= size; j += i + i) // 这里也要用long long
            isprime[j] = false;
}

解释一下 for (long long j = i * i; j <= 1000000; j += i + i) 这个循环

  1. 首先从 j += i + i 说起:
    首先在上面已经把所有的偶数位全部标记为 false,所以,当 isprime[i] 能进入在这个 for 循环上面 if 的时候,i 一定是个奇数。j 的起始值是 i * i 所以,起始值也一定是个奇数。
    所以 j 要一下加两个 i ,因为 j 加上一个 i 后一定是个偶数。
  2. 再说起始值为什么为是 i * i
    像上面所说, i 是个奇数。
    对于 i * (i - 1 - 2k) (k = 1, 2, 3…),因为 i - 1 是偶数,所以 i - 1 - 2k 一定是个偶数,所以 i * (i - 1 - 2k) 一定是个偶数,其所在位置一定标为false。
    对于i * (i - 2k) (k = 1, 2, 3…),i - 2k 一定是个奇数,对于任意的i * (i - 2k),一定有这样一个循环标记过 i * (i - 2k) 这个位置:
    for(long long j=(i - 2k) * (i - 2k); j <= 1000000; j += i + i)
    所以i * (i - 2k)一定是一个在之前的循环中已经被标记为 false 的位置。
    所以,i * (i - 1 - 2k) 与i * (i - 2k) (k = 1, 2, 3…)一定都是被标记过 false 的位置,所以直接略过,从 i * i 开始。

PS:注意循环要用 long long ,因为 i * i 可能会产生一个大于 int 类型的数,这时候用 int 类型变量接收会产生段错误。

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值