一般筛法求素数+快速线性筛法求素数
一般篩法求素數+快速線性篩法求素數
標簽:?正則表達式算法優化擴展c 2010-08-22 01:28?28738人閱讀?評論(8)?收藏?舉報 ?分類:版權聲明:本文為博主原創文章,轉載請注明出處。
目錄(?)[+]
TAG 素數? 數論
素數總是一個比較常涉及到的內容,掌握求素數的方法是一項基本功。
基本原則就是題目如果只需要判斷少量數字是否為素數,直接枚舉因子2 。。N^(0.5) ,看看能否整除N。
如果需要判斷的次數較多,則先用下面介紹的辦法預處理。
?一般的線性篩法
首先先介紹一般的線性篩法求素數
[cpp]?view plaincopy
- void?make_prime()??{????????
- ????memset(prime,?1,?sizeof(prime));??
- ????prime[0]=false;???????
- ????prime[1]=false;???????
- ????int?N=31700;????????
- ????for?(int?i=2;??i<N;??i++)???????????
- ??????if?(prime[i])?{????????????
- ????????primes[++cnt?]=i;???????
- ????????for?(int?k=i*i;?k<N;?k+=i)??????????
- ????????????prime[k]=false;?????????
- ??????}????????
- ????return;??
- }?????
這種方法比較好理解,初始時,假設全部都是素數,當找到一個素數時,顯然這個素數乘上另外一個數之后都是合數(注意上面的 i*i ,? 比 i*2 要快點 ),把這些合數都篩掉,即算法名字的由來。
但仔細分析能發現,這種方法會造成重復篩除合數,影響效率。比如10,在i=2的時候,k=2*15篩了一次;在i=5,k=5*6 的時候又篩了一次。所以,也就有了快速線性篩法。
?
快速線性篩法
快速線性篩法沒有冗余,不會重復篩除一個數,所以“幾乎”是線性的,雖然從代碼上分析,時間復雜度并不是O(n)。先上代碼
[cpp]?view plaincopy
- #include<iostream>??
- using?namespace?std;??????
- const?long?N?=?200000;?????
- long?prime[N]?=?{0},num_prime?=?0;??????
- int?isNotPrime[N]?=?{1,?1};?????
- int?main()??????
- {???????
- ????????for(long?i?=?2?;?i?<?N?;?i?++)?????????
- ????????{??????????????
- ????????if(!?isNotPrime[i])?????????????????
- ????????????prime[num_prime?++]=i;????
- ????????//關鍵處1??????????
- ????????for(long?j?=?0?;?j?<?num_prime?&&?i?*?prime[j]?<??N?;?j?++)??
- ????????????{?????????????????
- ????????????????isNotPrime[i?*?prime[j]]?=?1;????
- ????????????if(?!(i?%?prime[j]?)?)??//關鍵處2????????????????????
- ????????????????break;?????????????
- ????????}??????????
- ????}??????????
- ????return?0;?????
- }????
首先,先明確一個條件,任何合數都能表示成一系列素數的積。
?
不管 i 是否是素數,都會執行到“關鍵處1”,
①如果 i 都是是素數的話,那簡單,一個大的素數 i 乘以不大于 i 的素數,這樣篩除的數跟之前的是不會重復的。篩出的數都是 N=p1*p2的形式, p1,p2之間不相等
?
②如果 i 是合數,此時 i 可以表示成遞增素數相乘 i=p1*p2*...*pn, pi都是素數(2<=i<=n),? pi<=pj? ( i<=j )
p1是最小的系數。
根據“關鍵處2”的定義,當p1==prime[j] 的時候,篩除就終止了,也就是說,只能篩出不大于p1的質數*i。
?
我們可以直觀地舉個例子。i=2*3*5
此時能篩除 2*i ,不能篩除 3*i
如果能篩除3*i 的話,當 i' 等于 i'=3*3*5 時,篩除2*i' 就和前面重復了。
?
需要證明的東西:
- 一個數會不會被重復篩除。
- 合數肯定會被干掉。
根據上面紅字的條件,現在分析一個數會不會被重復篩除。
設這個數為 x=p1*p2*...*pn, pi都是素數(1<=i<=n)? ,? pi<=pj ( i<=j )?
當 i = 2 時,就是上面①的情況,
當 i >2 時, 就是上面②的情況, 對于 i ,第一個能滿足篩除 x 的數? y 必然為 y=p2*p3...*pn(p2可以與p1相等或不等),而且滿足條件的 y 有且只有一個。所以不會重復刪除。
證明合數肯定會被干掉? 用歸納法吧。
?類比一個模型,比如說我們要找出 n 中2個不同的數的所有組合 { i , j } ,1<=i<=n, 1<=j<=n,
我們會這么寫
for (i=1; i<n; ++i )
? for (j=i+1; j<=n; ++j)
?? {
??? /
?? }
我們取 j=i+1 便能保證組合不會重復。快速篩法大概也是這個道理,不過這里比較難理解,沒那么直觀。
?
1樓提供的方法,我整理下
//偶數顯然不行,所以先去掉偶數。可以看作上面第一種的優化吧。
//不過這種方法不太直觀,不太好理解。
?
[cpp]?view plaincopy- 我推薦這個算法!?易于理解。?只算奇數部分,時空效率都還不錯!??
- half=SIZE/2;???
- int?sn?=?(int)?sqrt(SIZE);???
- for?(i?=?0;?i?<?half;?i++)???
- ???p[i]?=?true;//?初始化全部奇數為素數。p[0]對應3,即p[i]對應2*i+3???
- for?(i?=?0;?i?<?sn;?i++)?{??????
- if(p[i])//如果?i+i+3?是素數??
- {???????
- ????for(k=i+i+3,?j=k*i+k+i;?j?<?half;?j+=k)???
- ????//?篩法起點是?p[i]所對應素數的平方?k^2??????????????????????????????????????????
- ????//?k^2在?p?中的位置是?k*i+k+i??
- ????//????下標?i?????????k*i+k+i??
- ????//對應數值?k=i+i+3???k^2???????????
- ???????p[j]=false;???
- }???
- }???
- //素數都存放在?p?數組中,p[i]=true代表?i+i+2?是素數。??
- //舉例,3是素數,按3*3,3*5,3*7...的次序篩選,因為只保存奇數,所以不用刪3*4,3*6....??
總結
以上是生活随笔為你收集整理的一般筛法求素数+快速线性筛法求素数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xdoj 1114(线段树离线处理)
- 下一篇: 美国电影大鱼高清资源