2016.1.25
学习完了伪线性的埃式筛法,我们来学习一个真线性的线性筛法。
时间复杂度:O(n)(即每个合数只会被筛一遍)
操作: 每当我们在外循环(外循环与埃式筛法相同)遇到一个素数时,我们将所有已筛得的素数(包括该素数)分别与该素数的乘积筛去;每当我们遇到一个合数时,则该合数可以表示为A=p1*p2*p3*……pk, pi<=pj (i<=j),那么我们筛去所有小于等于p1的素数分别与A的乘积。
代码如下:
int n,prime[1000005],e; bool is_prime[1000005];//is_prime为0表示为素数 void solve()//线性筛 { prime[0]=prime[1]=1; for(int i=2;i<=n;i++) { if(!is_prime[i]) prime[++e]=i; //无论i是否为素数都会执行到这一步,因为上述操作对于合数和素数是可以统一的 for(int j=1;j<=e && i*prime[j]<=n;j++) { is_prime[i * prime[j]]=1; if(i % prime[j] == 0) break;//若i为素数,则这步不会执行;若i为合数,则prime[j]等于i中最小质因数时停止。 } } }
这个操作执行起来并不难,代码也容易看懂,关键是证明。
关于为什么当遇到合数A时只筛小于等于p1的素数与A的乘积:比如我们筛了一个比p1大的素数pn与A的乘积即pn*p1*p2*p3*……pk,那么当我们遇到pn*p2*p3*p4*……*pk时会再次筛掉pn*p1*p2*p3*……pk。
关于为何合数都会被筛除并且只筛一次:对于我们每遇到一个合数时,则该合数可以表示为A=p1*p2*p3*……pk, pi<=pj (i<=j),那么在这之前我们遇到的能筛掉A的B必须等于p2*p3*……pk, pi<=pj (i<=j),因为根据我们的操作,A/B必须不大于B中任意一个质因数,所以B有且只有一个,所以每个合数会且只会被筛一次。
证毕