ACM数论-素数
ACM數論——素數?
?
素數定義:
? ? ? ? 質數(prime number)又稱素數,有無限個。質數定義為在大于1的自然數中,除了1和它本身以外不再有其他因數,這樣的數稱為質數。例 子:2、3、5、7、11、13、17、19。(那時候還有一種說法叫做“質數”,但是就語言上來說,我覺得“素數”這種叫法和“合數”比較搭配,類比于“化學元素”和“化合物”來看,叫“素數”非常貼切)
素數一些性質:
素數應用:
?判斷素數:
1 //判斷是否是一個素數 2 int IsPrime(int x) 3 { 4 if(x<=1) //0,1,負數都是非素數 5 return 0; 6 int ans=(int)sqrt(x)+1; /*計算枚舉上界,為防止ans值帶來的精度損失,所以采用根號值取整后再加1,即寧愿多枚舉一個,也不愿少枚舉一個數 */ 7 for(int i=2; i<ans; i++) 8 { 9 if(x%i==0) 10 { 11 return 0; 12 } 13 } 14 return 1; 15 } View Code?素數篩法:
1.開一個大的bool型數組prime[],大小就是n+1就可以了.先把所有的下標為奇數的標為true,下標為偶數的標為false.
2.代碼如下:
for( i=3; i<=sqrt(n); i+=2 ) { if(prime[i]) for( j=i+i; j<=n; j+=i )prime[j]=false;}??? 3.最后輸出bool數組中的值為true的單元的下標,就是所求的n以內的素數了。
??? 原理很簡單,就是當i是質(素)數的時候,i的所有的倍數必然是合數。如果i已經被判斷不是質數了,那么再找到i后面的質數來把這個質數的倍數篩掉。?
??? 一個簡單的篩素數的過程:n=30。
??? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
??? 第 1 步過后4 ... 28 30這15個單元被標成false,其余為true。
??? 第 2 步開始:
???? i=3; 由于prime[3]=true, 把prime[6], [9], [12], [15], [18], [21], [24], [27], [30]標為false.
???? i=4; 由于prime[4]=false,不在繼續篩法步驟。
???? i=5; 由于prime[5]=true, 把prime[10],[15],[20],[25],[30]標為false.
???? i=6>sqrt(30)算法結束。
??? 第 3 步把prime[]值為true的下標輸出來:
???? for(i=2; i<=30; i++)
???? if(prime[i]) printf("%d ",i);
??? 結果是 2 3 5 7 11 13 17 19 23 29
下圖為n=120的素數篩:
// 1:這是最原始的素數篩法 #define Max 1000000 bool prime[Max]; void IsPrime(){prime[0]=prime[1]=0;prime[2]=1;for(int i=3;i<max;i++)prime[i]=i%2==0?0:1;int t=(int)sqrt(Max*1.0);for(int i=3;i<=t;i++)if(prime[i])for(int j=i;j<Max;j+=i)prime[j]=0; } //2:優化后的篩法,手動地模擬原始篩法就可以發現,某個數字可能被不止一次地刪去 // 優化后的篩法就可以避免這種不必要的刪去操作 #define Max 1000000 bool prime[Max]; void IsPrime(){prime[0]=prime[1]=0;prime[2]=1;for(int i=3;i<max;i++)prime[i]=i%2==0?0:1;int t=(int)sqrt(Max*1.0);for(int i=3;i<=t;i++)if(prime[i])for(int j=i*i;j<Max;j+=2*i)//優化 prime[j]=0; } View Code?快速線性篩法?:
上面的方法比較好理解,初始時,假設全部都是素數,當找到一個素數時,顯然這個素數乘上另外一個數之后都是合數
把這些合數都篩掉,即算法名字的由來。但仔細分析能發現,這種方法會造成重復篩除合數,影響效率。
比如10,在i=2的時候,k=2*15篩了一次;在i=5,k=5*6 的時候又篩了一次。所以,也就有了快速線性篩法。
利用了每個合數必有一個最小素因子。每個合數僅被它的最小素因子篩去正好一次。所以為線性時間。
void get_prime() {int cnt = 0;for (int i = 2; i < N; i++){if (!tag[i]) p[cnt++] = i;for (int j = 0; j < cnt && p[j] * i < N; j++){tag[i*p[j]] = 1;if (i % p[j] == 0)break;}} } 函數模板 1 我推薦這個算法! 易于理解,只算奇數部分,時空效率都還不錯! 2 half=SIZE/2; 3 int sn = (int) sqrt(SIZE); 4 for (i = 0; i < half; i++) 5 p[i] = true;// 初始化全部奇數為素數。p[0]對應3,即p[i]對應2*i+3 6 for (i = 0; i < sn; i++) { 7 if(p[i])//如果 i+i+3 是素數 8 { 9 for(k=i+i+3, j=k*i+k+i; j < half; j+=k) 10 // 篩法起點是 p[i]所對應素數的平方 k^2 11 // k^2在 p 中的位置是 k*i+k+i 12 // 下標 i k*i+k+i 13 //對應數值 k=i+i+3 k^2 14 p[j]=false; 15 } 16 } 17 //素數都存放在 p 數組中,p[i]=true代表 i+i+2 是素數。 18 //舉例,3是素數,按3*3,3*5,3*7...的次序篩選,因為只保存奇數,所以不用刪3*4,3*6.... 推薦?
?
轉載于:https://www.cnblogs.com/slp0622/p/8998445.html
總結
- 上一篇: MapReduce过程详解及其性能优化
- 下一篇: 『电子书』分享一波码农必备编程开发类书籍