线性时间素数筛

求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;
}

你可能感兴趣的:(线性时间素数筛)