求N以内的所有素数时,素数筛很常用,普通的素数筛原理是所有素数的倍数(2倍以上)为合数。代码类似于:
#include<vector> #include<iostream> using namespace std; const int N=100; vector<bool>isp(N,true);//isp[i]表示i是否为素数 void prime_sieve() { for(int i=2;i<N;++i) if(isp[i]) for(int t,k=2;(t=i*k)<N;++k) isp[t]=false; } int main() { prime_sieve(); for(int i=2;i<N;++i) if(isp[i])cout<<i<<endl; }在这种方法中很多数都会被多次标记导致浪费时间,很显然想到每个合数能不能只标记一次呢?能,哪一次呢?最小素因子乘以最大因子那次。有几个点:
1、一个合数可能有很多因子分解形式,其中有一个最大因子a乘以最小素因子b的分解,其中必有b<=a,这种分解就叫特殊分解吧,这种分解只有一次。
2、线性筛的思想就是,假设使用该方法筛出的所有合数,都是因为特殊分解才被标记的,
3、具体过程是,遍历N内的数,对于当前被遍历的数x,现有的任一素数p都是在x之前遍历时找到的所以不大于x
4、p*x就表示一个以p为最小素因子以x为最大因子的合数H,这里H就是普通素数筛中提到的素数的倍数,问题是怎么保证H没有被表示为其他的素数倍数。
新的素数筛就是解决了第四点,其实也很简单,p整除x时就不应该继续筛下去了。什么意思呢,对于x,假设现有的所有素数为{2,3,5……pa,pb,pc…},每次要做的就是把所有素数乘以x得到的数记为合数,现在假设pa<pb<=x且pa整除x,当标记到pa*x时就应该停下来,不妨假设x=pa*k,如果继续标记的话下一个是pb*x=pb*pa*k=pa*(pb*k)=U,如果此时标记U的话,那么U就不是被特殊分解标记的,U的特殊标记的最小素因子应是pa,而不是pb。
代码如下:
#include<vector> #include<iostream> using namespace std; const int N=100; vector<bool>isp(N,true); void linear_prime_sieve() { vector<int>prime; for(int i=2;i<N;++i) { if(isp[i]) prime.push_back(i); for(int x:prime) { if(x*i>N)break; isp[x*i]=false; if(i%x==0)break; } } } int main() { linear_prime_sieve(); for(int i=2;i<N;++i) if(isp[i])cout<<i<<endl; }