质数筛选--快速筛选出小于等于N的质数

原创 2016年08月29日 20:38:30

Eratosthenes筛法(O(nlog^logn) )

此方法非常的简单:
1. 维护一个数组vector<bool> prime(n + 1, true)既让数组中的所有数都为真,表明它是质数。
2. 对于每一个质数,删除它的倍数,因为质数的倍数一定是合数。让后将数组中对应的数改为false,这样就可以避免遍历他。

代码如下:

int findPrime(int n) {
    vector<bool> prime(n + 1, true);
    int res=0;
    for (int i = 2; i <= n; i++) {
        if (prime[i]) {        //判断其是否是素数
            for (int j = 2; j*i <= n; j++)  //删除所有该素数的倍数
                prime[j*i] = false;
            res++;
        }
    }
    return res;
}

上述的筛选速度已经是很快了,但是他有一个问题。比如合数10,在枚举2的时候我们判定了一次,在枚举5的时候我们又判定了一次。因此使得其时间复杂度比O(n)要高

Eular质数筛法

与Eratosthenes筛法不同的是,对于外层枚举i,无论i是质数,还是是合数,我们都会用i的倍数去筛。但在枚举的时候,我们只枚举i的质数倍。比如2i,3i,5i,…,而不去枚举4i,6i…,原因我们后面会讲到。

此外,在从小到大依次枚举质数p来计算i的倍数时,我们还需要检查i是否能够整除p。若i能够整除p,则停止枚举。

利用该算法,可以保证每个合数只会被枚举到一次。我们可以证明如下命题:

假设一个合数k=M*p1,p1为其最小的质因子。则k只会在i=M,primeList[j]=p1时被筛掉一次。

首先会在i=M,primeList[j]=p1时被筛掉是显然的。因为p1是k的最小质因子,所以i=M的所有质因子也≥p1。于是j循环在枚举到primeList[j]=p1前不会break,从而一定会在i=M,primeList[j]=p1时被筛掉

其次不会在其他时候被筛掉。否则假设k在i=N, primeList[j]=p1时被筛掉了,此时有k=N*p2。由于p1是k最小的质因子,所以p2 > p1,M > N且p|N。则i=N,j枚举到primeList[j]=p1时(没到primeList[j]=p2)就break了。所以不会有其他时候筛掉k。

同时,不枚举合数倍数的原因也在此:对于一个合数k=M*2*3。只有在枚举到i=M*3时,才会计算到k。若我们枚举合数倍数,则可能会在i=M时,通过M*6计算到k,这样也就造成了多次重复计算了。

综上,Eular筛法可以保证每个合数只会被枚举到一次,时间复杂度为O(n)。当N越大时,其相对于Eratosthenes筛法的优势也就越明显。

代码如下:

int findPrime1(int n) {
    vector<bool> prime(n + 1, true);
    vector<int> primeList(n+1); int primeCount = 0;
    for (int i = 2; i <= n; i++) {
        if (prime[i]) {
            primeCount++;
            primeList[primeCount] = i;
        }
        for (int j = 1; j <= primeCount; j++) {
            if (i*primeList[j] > n) break;
            prime[i*primeList[j]] = false;
            if (i%primeList[j] == 0) break;
        }
    }
    return primeCount;
}

对于两者的时间测试来看,其差别是非常小的:

int main() {
    clock_t start = clock();
    cout << findPrime1(5000000) << endl; 
    clock_t finish = clock();
    cout<<(double)(finish - start) / CLOCKS_PER_SEC<<endl;        //11.854s
    clock_t start2 = clock();
    cout << findPrime1(5000000) << endl;
    clock_t finish2 = clock();
    cout << (double)(finish2-start2) / CLOCKS_PER_SEC << endl;     //11.81s
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

三种素数筛选法详解 (转)

转自:http://tr0217.blog.163.com/blog/static/3606648020099302135503/ 第一种:剔除2 3 4 5 6 ... ... 的倍数 ...

筛选法求素数算法

筛选法生成质数表(素数表)的基本思想如下: 假设有一个数组存放整数2 ~ N,如下所示: 首先将2的倍数筛去(实际操作时可以将数组对应的值设置为0),得到: 然后将3...

51Nod-1181-质数中的质数(质数筛法)

ACM模版描述如果一个质数,在质数列表中的编号也是质数,那么就称之为质数中的质数。例如:3 5分别是排第2和第3的质数,所以他们是质数中的质数。现在给出一个数N,求>=N的最小的质数中的质数是多少(可...
  • f_zyj
  • f_zyj
  • 2016年07月26日 18:10
  • 492

java 找出小于数字N的所有素数

java找出小于数字N的所有素数

c语言:实现对于给定的正整数N,依次打印出小于等于N的所有素数。两种方法及其优化

请编写一个程序,实现对于给定的正整数N,依次打印出小于等于N的所有素数。方法一:试除法由素数的定义得到如下程序:#includeint print_prime(int num)//prime表示素数{...

【质数算法】——判断质数、求小于N的质数、求前N个质数

质数(prime number)又称素数,一个大于1的自然数,除了1和它本身外,不能被其他自然数整除。 场景一:输入一个整数n,判断n是否为质数。 思路:判断是否大于或等于2,如果是则对遍历2到根号n...

素数筛选法,快速获取素数序列

今天,学习了[素数][6]求取的方法,感觉很棒,拿来分享一下。首先,对比一下两种方法—普通求取素数的方法和基于筛选法的素数求取方法。...

2017百度秋招真题笔试之十字架

题目描述小A很喜欢十字架,并喜欢用各种方式产生十字架的样子。小A不满足简单的十字架输出效果,所以重新定义了一种高大上的十字架输出方式。给一个n(1≤n≤10) , 输出对应的图形,具体形式如下: 1....
  • hnu2012
  • hnu2012
  • 2017年04月11日 10:32
  • 1041

c++中的int和float存储编码方法

先复习一下十进制和二进制转换的知识点,以及小端大端的知识点: 十进制数与二进制数的转换 http://course.cug.edu.cn/21cn/%E7%94%B5%E5%AD%90%E...
  • sonikk
  • sonikk
  • 2013年05月25日 18:54
  • 3625

动态规划求小于等于n的质数个数

动态规划求小于等于n的质数个数
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:质数筛选--快速筛选出小于等于N的质数
举报原因:
原因补充:

(最多只允许输入30个字)