欧拉筛法实现素数表的快速筛取

引子:
我如今想知道1——1e8的范围内有多少个素数(质数),有什么方法?
朴素法,对于每个数n,咱们判断它是不是素数
像这样:c++

bool check(int curr)
{
    int i;
    for(i = 2; i < sqrt(curr)+1; ++i)
    {
        if(curr % i == 0) return false;
    }
    return true;
}

对于每个数,咱们最多须要sqrt(n)次即可以断定出它是素数仍是合数。
太慢了!这个时间复杂度已经能够飞出天际了。markdown


咱们须要一种快速的方法筛取素数。
咱们观察素数和合数最大的不一样——合数有多个因数(除了1与自身),而素数不行。那么当咱们得知对于int x,2 * x ,3 * x……便必定都是合数
看来这个效率很高,可是仍是不够快。由于会出现判断重复ui

那么咱们便使用欧拉筛法来筛取素数。
前文的方法作了什么无用功?例如20的判断,咱们在2,4,5,10的时候都去断定了一次,而欧拉筛法能够保证20 只被断定1次
如何保证?spa


先放出源代码:code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000005;

int n, x[maxn], pri[maxn/10], tot, curr, num;//m之内的质数不会m/10个,不放心 || m很小的话能够再开大一点

int main()
{
    //freopen("test.in", "r", stdin);
    scanf("%d%d", &n, &num);
    x[1] = 1;
    for(int i = 2; i != n+1; ++i)
    {
        if(!x[i])
        {
            pri[tot++] = i; //若是一个数是合数那么它必定会在以前被断定出来,故剩下的必定是质数
        }
        for(int j = 0; j != tot; ++j)//当前的全部质数
        {
            curr = pri[j]*i;//必定是合数
            if(curr > n)    break;//超出范围
            x[curr] = 1;//置为合数
            if(i % pri[j] == 0)    break;//重点
        }
    }
    fclose(stdin);
    return 0;
}

重点代码:string

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

要说保证某数不被重复判断,关键就在这行代码上。
知足上式时,i是pri[j]的倍数,那么对于后面的pri[k]来讲,i * pri[k]就能够被分解为pri[j] * (i / pri[j] *pri[k]),而该数在以前i == pri[j]的时候已经出现过,因而就重复了。直接退出,避免了重复断定it


欧拉筛法当然快,可是也有适用条件,例如求(n, n + 10)中的素数个数的时候,它就明显慢了。因为必须从1开始,因此欧拉筛法的用处在于直接打表而不是求某一段区间。对于这种问题,朴素法何尝不可class

欧拉筛法的原理分析至此结束。
箜瑟_qi 2017.04.10 00:43test

相关文章
相关标签/搜索