線性時間素數篩

求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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章