日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

求素数: 一般线性筛法 + 快速线性筛法

發布時間:2023/12/9 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 求素数: 一般线性筛法 + 快速线性筛法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

From: http://blog.csdn.net/dinosoft/article/details/5829550

素數總是一個比較常涉及到的內容,掌握求素數的方法是一項基本功。

基本原則就是題目如果只需要判斷少量數字是否為素數,直接枚舉因子2 。。N^(0.5) ,看看能否整除N。

如果需要判斷的次數較多,則先用下面介紹的辦法預處理。


?一般的線性篩法

首先先介紹一般的線性篩法求素數

[cpp] view plaincopyprint?
  • 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 plaincopyprint?
  • #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 plaincopyprint?
  • 我推薦這個算法!?易于理解。?只算奇數部分,時空效率都還不錯!??
  • 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....??

  • 擴展閱讀

  • 打印質數的各種算法 http://coolshell.cn/articles/3738.html? 里面有個用C++模板實現的,純屬開闊眼界,不怎么實用。
  • 檢查素數的正則表達式? http://coolshell.cn/articles/2704.html? 數字n用? 1111。。1 (n個1)表示,純屬坑爹。

  • ===========================================================================

    以上完整源碼(a.cpp)

    /*功能: 求[0, 20000)間的所有素數, 假設內存空間足夠環境: Linux C++編譯: g++ -o a a.cpp -Wall -Os總結: 這兩種算法在性能上差距不是很大, 當N個數較少時, 還是"一般線性篩選法"速度更快,但是當N較大時, 快速線性篩選法的優勢更加明顯 */ #include <stdio.h> #include <string.h>#define N 20000// 一般線性篩選法: 會出現重復篩選同一個數 void make_prime(int primes[], int& cnt) {bool bPrime[N]; // 質數標志數組cnt = 0; // 素數個數memset(bPrime, true, sizeof(bPrime));// 假設全是素數bPrime[0] = false; // 0: 非素數bPrime[1] = false; // 1: 非素數for (int i = 2; i < N; i++){if (bPrime[i]) // i是素數{primes[cnt++] = i; // 將素數i保存到bPrimes[]中// 作篩選: i的倍數都不是素數for (int k = i * i; k < N; k += i) // 將素數i的倍數全置為非素數標志bPrime[k] = false;}} }// 快速線性篩選法: 不會出現重復篩選同一個數 void getPrimes(int primes[], int& cnt) {bool bPrime[N]; // 素數標志數組cnt = 0; // 素數個數memset(bPrime, true, sizeof(bPrime)); // 假設全部為素數bPrime[0] = false; // 0: 非素數bPrime[1] = false; // 1: 非素數for(int i = 2; i < N; i++){if(bPrime[i]) // i是素數primes[cnt++] = i; // 保存素數i// 作篩選: i與素數的乘積都不是素數for(int j = 0; j < cnt && i * primes[j] < N; j++){bPrime[i * primes[j]] = false; // 置非素數標志if(i % primes[j] == 0) // i為素數的倍數break;}} }int main() {int primes[N]; // 保存所有素數int cnt = 0; // 素數個數#if 1make_prime(primes, cnt); // 調用一般線性篩選法 #elsegetPrimes(primes, cnt); // 調用快速線性篩選法 #endiffor(int i = 0; i < cnt; i++)printf("primes[%d] = %d\n", i, primes[i]);printf("\n素數個數cnt=%d\n", cnt);return 0; }


    ?

    總結

    以上是生活随笔為你收集整理的求素数: 一般线性筛法 + 快速线性筛法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。