1. 程式人生 > 實用技巧 >素數篩法

素數篩法

入土

初識c++的素數篩舉,n2暴力,列舉每一個數,如果這個數的約數只有兩個,它就為質數

入門

n2時,只判斷到根號就剎車

經典

埃式篩法

O(nloglogn)

基本思想:從2開始,將每個質數的倍數都標記成合數,以達到篩選素數的目的

node
int vis[maxn];  
void Prime(){
    memset(vis,0);           //初始化都是素數
    vis[0] = vis[1] = 1;  //0 和 1不是素數
    for (int i = 2; i <= maxn; i++) {
        if (!vis[i]) {         //如果i是素數,讓i的所有倍數都不是素數
            for (int j = i*i; j <= maxn; j += i) { 
                vis[j] = 1;
            }
        }
    }

缺陷:
對於一個合數,有可能被篩多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……
那麼如何確保每個合數只被篩選一次

進階

尤拉篩

O(n)

基本思想:在埃氏篩法的基礎上,讓每個合數只被它的最小質因子篩選一次,以達到不重複的目的

node
int prime[maxn];
int visit[maxn];
void Prime(){
    memset(visit,0);
    memset(prime, 0);
    for (int i = 2;i <= maxn; i++) {
        if (!visit[i]) {
            prime[++prime[0]] = i;      //紀錄素數, 這個prime[0] 相當於 cnt,用來計數
        }
        for (int j = 1; j <=prime[0] && i*prime[j] <= maxn; j++) {
            visit[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}

1.對於visit[i*prime[j]] = 1 的解釋:

這裡不是用 i 的倍數來消去合數,而是把 prime裡面紀錄的素數,升序來當做要消去合數的最小素因子
用 i 來當倍數

2.對於 i % prime[j] == 0 就break的解釋 :

當 i 是 prime[j]的倍數時,i = k * prime[j],如果繼續運算 j+1,i * prime[j+1] = prime[j] * k * prime[j+1],

這裡prime[j]是最小的素因子,當i = k * prime[j+1]時會重複,所以才跳出迴圈

舉個例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出迴圈,prime[j+1] = 3,8 * 3 = 2 * 4 * 3 = 2 * 12,

在i = 12時會計算,因為尤拉篩法的原理便是通過最小素因子來消除。