筛法求素数:
素数总是一个比较常涉及到的内容,掌握求素数的方法是一项基本功。
基本原则就是题目如果只需要判断少量数字是否为素数,直接枚举因子2。。N^(0.5),看看能否整除N。
如果需要判断的次数较多,则先用下面介绍的办法预处理。
筛选法的思路是:
要求10000以内的素数,把1-10000都列出来,1不是素数,划掉;2是素数,所有2的倍数都不是素数,划掉;取出下一个幸存的数,划掉它的所有倍数;直到所有素数找完为止。
这种做法的空间复杂度是O(n),时间复杂度O(n/logn)。
const int Max = 1000005; bool prime[Max]={0};//0表示素数,1为非素数 //筛选n以内的素数 void getPrime(int n) { <pre name="code" class="cpp"> prime[0]=0; prime[1]=0;int i,j; int t; for(i = 2; i <= n; i++) { if(!prime[i]) { for(j = 2; (t=j*i) <= n; j++) prime[t] = 1; } } }
这种方法比较好理解,初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数(注意上面的 i*i , 比 i*2要快点 ),把这些合数都筛掉,即算法名字的由来。
但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。比如10,在i=2的时候,k=2*15筛了一次;在i=5,k=5*6的时候又筛了一次。所以,也就有了快速线性筛法。
快速线性筛法没有冗余,不会重复筛除一个数,所以“几乎”是线性的,虽然从代码上分析,时间复杂度并不是O(n)。先上代码
#include<iostream> using namespace std; const long N = 200000; long prime[N] = {0},num_prime = 0; int isNotPrime[N] = {1, 1}; int main() { for(long i = 2 ; i < N ; i ++) { if(! isNotPrime[i]) prime[num_prime ++]=i; //关键处1 for(long j = 0 ; j < num_prime && i * prime[j] < N ; j ++) { isNotPrime[i * prime[j]] = 1; if( !(i % prime[j] ) ) //关键处2 break; } } return 0; }
首先,先明确一个条件,任何合数都能表示成一系列素数的积。
不管 i是否是素数,都会执行到“关键处1”,
①如果 i都是是素数的话,那简单,一个大的素数 i乘以不大于 i的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等
②如果 i是合数,此时 i可以表示成递增素数相乘 i=p1*p2*...*pn, pi都是素数(2<=i<=n), pi<=pj ( i<=j )
p1是最小的系数。
根据“关键处2”的定义,当p1==prime[j]的时候,筛除就终止了,也就是说,只能筛出不大于p1的质数*i。
我们可以直观地举个例子。i=2*3*5
此时能筛除 2*i ,不能筛除 3*i
如果能筛除3*i的话,当 i'等于 i'=3*3*5时,筛除2*i'就和前面重复了。
参考练习题: http://acm.nyist.net/JudgeOnline/problemset.php?typeid=10