筛法是一种高效的构造素数表的算法。
在给出代码前先介绍一个数论的定理。设a>1,则a=p1^n1*p2^n2...pk^nk.其中p1、p2...pk是质数,n1、n2...是正整数,并且在不计顺序的情况下,该表示是唯一的。
先介绍一个简单的近似线性的筛法Eratosthenes筛法(埃拉托斯特尼筛法)
先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去......。
(图片来自维基百科)
1 void get_prime1() 2 { 3 memset(is_prime,true,sizeof(is_prime)); 4 int tot=0; 5 long long i,j; 6 for(i=2;i<=(int)sqrt(MAX*1.0);i++) 7 { 8 if(is_prime[i]) 9 { 10 prime[tot++]=i; 11 for(j=i*2;j<=MAX;j+=i) 12 is_prime[j]=false; 13 } 14 } 15 }
i<=(int)sqrt(MAX*1.0)是因为如果a是一个合数,则a必有一个小于等于sqrt(a)的素因子.
这种算法效率较高但是有些数会被重复筛去如12在i=2和i=3是都被筛去下面介绍一种每个合数只被筛一次的算法
Euler筛法(欧拉筛法)
1 void get_prime2() 2 { 3 memset(is_prime,true,sizeof(is_prime)); 4 int tot=0; 5 long i,j; 6 for(i=2;i<=MAX;i++) 7 { 8 if(is_prime[i]) prime[tot++]=i; 9 for(j=0;j<tot && prime[j]*i<MAX;j++) 10 { 11 is_prime[i*prime[j]]=false; 12 if(i%prime[j]==0) break;//关键点 13 } 14 } 15 }
由最之前的定理可知每一个合数都可以写成素数与其他数相乘的形式,a=pmin*n,pmin为a的最小素因子且这种表达是唯一的,关键点就是利用了这一性质将每一个合数都只被其最小素因子筛去.is_prime[i*prime[j]]=false;只执行了N次,所以此算法的时间复杂度为O(n).
References
维基百科:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
http://blog.csdn.net/dinosoft/article/details/5829550
http://www.cnblogs.com/xwdreamer/archive/2011/03/21/2297040.html