《永劫無間》8月9日更新內容一覽
判斷某個整數n是否為素數,或者說求某個範圍內的素數個數等等,這些是用到素數篩的最最基礎的問題。
判斷一個整數n是否為素數
首先i從2開始列舉到 \(\sqrt{n}\) ,然後一旦n可以被i整除,就返回false,然後如果i列舉完了,n都沒能整除i,那就證明是素數,返回true。
bool isprime(int n){ //判斷單個數n是否為素數
if(n <= 1) return 0;
for(int i=2;i*i<=n;i++)
if(n % i == 0) return 0;
return 1;
}
埃氏篩法
作用就是列舉n以內的素數
首先將2到n範圍內所有的整數寫下來,在這其中最小的數字2是素數,將表中所有2的倍數都劃去,表中剩餘最小的數字是3,它的因數只有它自己,(它已經最小了),然後就再把表中所有3的倍數都劃去,以此類推,每次表中剩餘的最小數字是m,m就是素數,然後將表中所有m的倍數都劃去,這樣反覆操作之後就能依次列舉n以內的素數。
模板(數字n的作用就是篩出從1-n的素數,prime陣列存這些素數,isprime[ i ]如果為1,則i是素數):
int prime[n],isprime[n]; int eratosthenes(int n){ int p = 0; for(int i=0;i<=n;i++) isprime[i] = 1;//先假設這些數都是素數,後面再由它們的因數來劃掉。 isprime[0] = isprime[1] = 0; for(int i=2;i<=n;i++){ if(isprime[i]){ prime[p++] = i; for(int j = 2 * i;j <= n;j += i) isprime[j] = 0; } } return p; }
埃氏篩法有一個缺陷,每次都要去對當前表內的素數m的所有的倍數進行刪掉,也就是“visit[ j ] = 0” 這個操作,對於一個合數,它很有可能會被很多個素數篩到,也就重複進行了很多次操作,在此基礎之上再次進行改進,我們還有尤拉篩法:
尤拉篩法
尤拉篩法並不是用目前最小素數的所有倍數來篩合數,而是要篩去一個合數時,用這個合數的最小質因子來篩去它。
尤拉篩法建立了一個數組prime,然後它的下標從1開始依次存的是這個區間的從小到大的素數,然後在外層迴圈for中讓i累加,這裡的i其實就是“倍數”的含義,然後迴圈的時候,取出陣列中所有數,然後從小到大地對於i * 這些合數得到的值篩掉(它們都是合數)然後呢,i一直在增加,每一次執行這個篩操作之前,我們都要先判斷一下i是否為素數,若是的話就把i加入前面的陣列prime之中。
在這個篩操作中,還有一個跳出條件需要注意:if (i % prime[j] == 0) break;
這裡跳出迴圈的意思是:當i是prime[ j ]的倍數時,i = k * prime[ j ] 如果進行下次迴圈,j 會加一指到prime陣列中的下一個素數,下一次迴圈中 會出現 i * prime[ j+1 ] 而因為i是prime[ j ] 的倍數,所以上式變為 prime[j] * k * prime[j+1] ,這個表示的是一個合數,而這個合數的最小素因子其實是prime[ j ] ,而不是 prime[j+1] 。也就是說這個時候要篩掉的這個數在前面已經被篩掉了,因為它有一個更小的素因子,所以這已經是一次重複的篩運算了。然後接下來的j遞增的內層迴圈中,i 一直是不變的,所以後面所有的i * prime[ j ] 都是會重複的,故至此跳出迴圈。接下來就進行下一次i的迴圈了。
下面prime[0] 表示有多少個素數,然後從陣列的下標1開始存第一個素數,以此類推
程式碼:
const int maxn = 100;
int prime[maxn];
int vis[maxn];
void eularsieve(){
memset(vis,0,maxn * sizeof(int));//是0表示i是素數
memset(prime,0,maxn * sizeof(int));
for(int i=2;i<maxn;i++){
if(!vis[i])
prime[++prime[0]] = i;
for(int j=1;j <= prime[0] && i * prime[j] <= maxn;j++){
vis[i*prime[j]] = 1;
if(i % prime[j] == 0)
break;
}
}
}