1. 程式人生 > >埃氏篩法+線性篩法+杜教篩+min25篩總結

埃氏篩法+線性篩法+杜教篩+min25篩總結

埃氏篩法

這個篩法是最樸素的篩法了,可以在 O ( n l o g l o g n

) 的時間內(基本 O ( n ) )篩出[1,n]中所有素數。實現非常簡單,從2開始遍歷,對於每個質數都暴力算出它的所有倍數並篩掉,根據尤拉的調和級數定理,這個時間是
O ( n l o g n )
級別的,但是隻有質數才需要計算倍數,然後不知怎麼回事複雜度就變成 O ( n l o g l o g n ) 了。

const int maxn = 100005, N = 100000;
int vis[maxn];
for(int i = 2; i <= N; i++) if(!vis[i])
    for(int j = i + i; j <= N; j += i) vis[j] = 1;

最終vis陣列中值為0的即為素數。

應用:給定n,m,求區間[n,m]中的質數個數。 n m 10 12 , m n 10 6
首先我們會發現,如果區間中某個數不是質數,那麼它必然有一個小於1E6的質因子。於是我們可以把[1,1E6]範圍內的質數全部篩出來,然後用這些質數去篩區間[n,m]中的數字,複雜度應該是 O ( ( m n ) l o g l o g ( m n ) ) 。程式碼就不放了。

線性篩(尤拉篩法)

顧名思義,它可以線上性時間內篩出區間內質數及積性函式的值。但是在n小於1E6的時候甚至比埃氏篩法慢(因為常數大),於是它的主要功能就變成了求積性函式的值。
考慮如何做到線性。如果我們把每個數字都用它的最小質因子篩去的話就行了,但是怎麼做呢?
從小到大列舉每個數字,再列舉當前已經篩出來的質數,篩掉數字乘質數的值。如果數字是當前質數的倍數就可以退出了,因為質數繼續列舉就不是最小質因子了。因此此時效率為 O ( n ) 。程式碼如下:

const int maxn = 100005, N = 100000;
int vis[maxn], prime[maxn], cnt;
for(int i = 2; i <= N; i++){
    if(!vis[i]) prime[++cnt] = i;
    for(int j = 1; j <= cnt; j++){
        int p = prime[j], mul = p * i;
        if(mul > N) break;
        vis[mul] = 1;
        if(i % p == 0) break;
    }
}

上面說過了,這個東西還可以篩出積性函式的值。積性函式定義為對於任意互質的數對 i , j f ( i ) f ( j ) = f ( i j ) 。我們上面實際上算出了每個數的最小質因子,當然也可以算出積性函式的值了,當然我們需要計算任意的質數以及單個質數的冪次對應的f的值,因為這樣才可以利用質因數分解計算出任意數字的f。

const int maxn = 100005, N = 100000;
int f[maxn], pw[maxn], vis[maxn], prime[maxn], cnt;
//pw[i]為i最小質因子的次數
for(int i = 2; i <= N; i++){
    if(!vis[i]){
        prime[++cnt] = i;
        pw[i] = 1;
        f[i] = ...;//要求給出i為質數時f(i)的值
    }
    for(int j = 1; j <= cnt; j++){
        int p = prime[j], mul = p * i;
        if(mul > N) break;
        vis[mul] = 1;
        if(i % p == 0){
            f[mul] = f[i / pow(p, pw[i])] * f[pow(p, pw[i] + 1)];//要求給出i為質數的整數次冪時f的值
            pw[mul] = pw[i] + 1;
            break;
        } else pw[mul] = pw[i] * pw[p];
    }
}

當然了,具體計算的時候可以用一些方法把pow去掉。接下來就來列舉一些積性函式的求值:

尤拉函式

φ ( n ) = 小於等於n且與n互質的數的個數。定理:

φ ( n ) = n p i 1 p i , n = p i e i , p i
因此我們會發現,若p為n的一個質因子,則有 φ ( n p ) = p · φ ( n ) .當p為質數時 p h i ( p ) = p 1 。於是就可以愉快地寫篩法了:

const int maxn = 100005, N = 100000;
int vis[maxn], phi[maxn], prime[maxn], cnt;
phi[1] = 1;
for(int i = 2; i <= N; i++){
    if(!vis[i]){
        prime[++cnt] = i;
        phi[i] = i - 1;
    }
    for(int j = 1; j <= cnt; j++){
        int p = prime[j], mul = p * i;
        if(mul > N) break;
        vis[mul] = 1;
        if(i % p == 0){
            phi[mul] = phi[i] * p;
            break;
        } else phi[mul] = phi[i] * phi[p];
    }
}

於是我們就可以在 O ( n ) 的時間內處理出[1,n]中所有數字的尤拉函式值了。

莫比烏斯函式

μ ( n ) = { 1 n = 1 ( 1 ) k n = p 1 p 2 p k , p i 0 o t h e r s
當n含有平方因子時函式值為0,n為質數時函式值為1,於是也可以線性篩了。

const int maxn = 100005, N = 100000;
int vis[maxn], mu[maxn], prime[maxn], cnt;
mu[1] = 1;
for(int i = 2; i <= N; i++){
    if(!vis[i]){
        prime[++cnt] = i;
        mu[i] = -1;
    }
    for(int j = 1; j <= cnt; j++){
        int p = prime[j], mul = p * i;
        if(mul > N) break;
        vis[mul] = 1;
        if(i % p == 0) break;//mul含有平方因子,mu值為0
        mu[mul] = -mu[i];//實際上是mu[mul] = mu[i] * mu[p];
    }
}

於是我們的線性篩就可以用來做題了!

例題

i = 1 n j = 1 n g c d ( i , j )
求出上式的值,其中 n 10 6
根據尤拉函式的性質 d | n φ ( d ) = n ,可以化簡上式得到