短小精悍的線性時間素數篩法

輸入n,求n以內的所有素數

算法用兩個數組存儲數據:
一個是prime[],存儲n以內所有的素數,其index爲pi,初值爲0
一個是is_prime[i],表示自然數i(i<=n)是不是質數。
算法如下:
linear_prime_sieves
1:  set is_prime[] to true
2:  for i=2 to n
3:    if is_prime[i]=true then prime[pi++]=i
4:    for j=0 to pi-1
5:      if prime[j]*i>n then exit loop_j
6:      is_prime[prime[j]*i]=false
7:      if i mod prime[j]=0 then exit loop_j
8:    endif
9:  endif

    這個算法有兩層循環,第一層遍歷2到n之間的所有自然數i,看看它是不是質數,如果是,則把i放進prime數組中。第二層循環是對所有未來的數進行篩選。對於當前正在處理的i,顯然它乘以任何一個已經找到的素數的結果肯定是和數,它們將會被剔除。整個算法最核心的一句是第7行:當i可以被某個已經找到的質數整除的時候,循環退出,不再進行剔除工作。這樣做的原因是:當prime[j]是i的因子的時候,設i=prime[j]*k,首先,我們可以肯定的說,prime[j]是i的最小質因數,這是因爲第二層循環是從小到大遍歷素數的;其次,我們可以肯定的說,i已經無需再去剔除prime[j']*i (j'>j) 形式的和數了,這是因爲,prime[j']*i可以寫成prime[j']*(prime[j]*k)=prime[j]*(prime[j']*k),也就是說所有的prime[j']*i將會被將來的某個i'=prime[j']*k剔除掉,當前的i已經不需要了。
    然後我們再看看時間複雜度。雖然有兩層循環,但是我們發現,正因爲有了第7句,所有is_prime數組裏面的所有false都只被賦值了一次,而且是在發現它的最小質因數的時候被賦值的。在外層循環執行了O(n)次操作的同時,內層循環裏面總的操作次數也是O(n)次。因此,總的時間複雜度是O(n)。因爲用了is_prime數組,空間複雜度也爲O(n)。
    有了這個算法,我們還能很容易地知道一些信息。先把每個自然數表示成素數冪的形式:i=p1^n1*p2^n2*...*pk^nk,其中p1,p2...pk爲自然數i的所有質因數,n1,n2...nk表示每個質因數的個數。由於算法中每個和數都是在發現它的最小質因數的時候找到的,我們能夠很容易的知道每個數的最小質因數、最小質因數的個數、以及不重複的質因數個數。
    這個算法給我的感覺是相當的短小精悍。這已經是上個世紀80年代的作品了。reference如下:"P. Pritchard. Linear prime-number sieves: A family tree. Science of Computer Programming, 9:17-35, 1987"。當然,它也是有缺點的,最大的問題就在於空間複雜度爲O(n),佔用了過多的空間。雖然可以用bit操作把is_prime數組縮小32倍,但是如果那樣的話,上面提到的那些附加信息,如每個數的最小質因數,就沒法得到了。


以上部分轉載自:http://blog.csdn.net/mysword/article/details/5122855

下面附上我的篩法求素數代碼

void init_prime()
{
    memset(isprime,0,sizeof(isprime));
    isprime[0]=isprime[1]=1;
    for(int i=2;i<MAXN;++i)
        {
            if(!isprime[i]) prime[pnum++]=i;
            for(int j=0;j<pnum && prime[j]*i<MAXN;++j)
            {
                isprime[prime[j]*i]=1;
                if(i%prime[j]==0) break;
            }
        }
}
發佈了39 篇原創文章 · 獲贊 30 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章