「算法筆記」線性篩


一、Etratosthenes 篩法

任意整數 \(x\) 的倍數 \(2x,3x,\cdots\) 都不是質數。考慮從 \(2\) 開始,由小到大掃描每個數 \(x\),把它的倍數 \(2x,3x,\cdots,\lfloor \frac{n}{x}\rfloor \times x\) 標記為合數。當掃描到一個數時,若它尚未被標記,則它不能被 \(2\sim x-1\) 之間的任何數整除,該數就是質數。

另外,可以發現,\(2\)\(3\) 都會把 \(6\) 標記為合數。實際上,小於 \(x^2\)\(x\) 的倍數在掃描更小的數時就已經被標記過了。可以對 Etratosthenes 篩法進行優化,對於每個數 \(x\),只需要從 \(x^2\) 開始,把 \(x^2,(x+1)\times x,\cdots,\lfloor \frac{n}{x}\rfloor \times x\) 標記為合數即可。

memset(vis,0,sizeof(vis));    //合數標記 
for(int i=2;i<=n;i++){
    if(vis[i]) continue;
    p[++cnt]=i;    //i 是質數 
    for(int j=i;j<=n/i;j++) vis[i*j]=1;
}

時間復雜度:\(\mathcal O(n\log \log n)\)

二、線性篩法

可以發現,即使在優化后(從 \(x^2\) 開始),Eratosthenes 篩法仍然會重復標記合數。如果能讓每個合數都只被標記一次,那么時間復雜度就可以降到 \(\mathcal O(n)\) 了。

每個數被它最小的質因子篩一次。

vis[0]=vis[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) p[++cnt]=i;
    for(int j=1;j<=cnt&&i*p[j]<=n;j++){
        vis[i*p[j]]=1;
        if(i%p[j]==0) break;
    }
} 
  • 每個數被它的最小質因子篩掉。每次篩去 \(i\times p[j]\)\(p[j]\) 是這個數的最小質因子。
  • 如果 \(i\mid p[j]\) 說明 \(i\times p[j]\) 有兩個 \(p[j]\) 的質因子,並且 \(p[j]\)\(i\times p[j]\) 的最小質因子。若再枚舉,則 \(p[j]\) 就不是 \(i\times p[j]\) 的最小質因子了。所以篩過一次 \(p[j]\) 就 break。

三、篩積性函數

1. 一些定義

數論函數是指一個正整數到整數的映射。

積性函數:對於所有 互質 的整數 \(a,b\),有性質 \(f(ab)=f(a)f(b)\) 的數論函數。\(f(1)=1\)

完全積性函數:對於所有整數 \(a,b\),有性質 \(f(ab)=f(a)f(b)\) 的數論函數。

2. 常見的積性函數

約數個數函數  \(d(n)=\sum_{d|n} 1\)

約數和函數  \(\sigma (n)=\sum_{d|n} d\)

約數 \(k\) 次冪函數  \(\sigma _k (n)=\sum_{d|n} d^k\)

歐拉函數  \(\varphi (n)=\sum_{i=1}^n [\gcd(i,n)=1]\)

莫比烏斯函數  \(\mu (n)=\begin{cases}1&{n=1}\\(-1)^k&c_{1,2,...,k}=1\ (n=\prod_{i=1}^k p_i^{c_i})\\0&c_i>1\end{cases}\)

四、常見線性篩

1. 求約數和

給定 \(n\),求 \(f(1),f(2)...f(n)\)。其中 \(f(x)=\sum_{d|x} d\)

證明約數和函數是積性函數:

考慮 \(a,b\),並且 \(\gcd(a,b)=1,ab=x\)

\(f(a)f(b)=\sum_{d\mid a}d\sum_{p\mid b}p=\sum_{d\mid a}\sum_{p\mid b}dp=\sum_{(dp)\mid (ab)} dp=\sum_{t\mid x}t=f(x)\)

其中,因為 \(\gcd(a,b)=1\),則 \(\gcd(d,p)=1\)

線性篩求約數和函數:

根據唯一分解定理,可得:\(n=p_1^{c_1}\times p_2^{c_2}\times \cdots \times p_k^{c_k}=\prod\limits_{i=1}^k p_i^{c_i}\)

\(\sigma (x)=(1+p_1+p_1^2+\cdots+p_1^{c_1})\times (1+p_2+p_2^2+\cdots p_2^{c_2})\times \cdots\times (1+p_k+p_k^2\cdots p_k^{c_k})\)

\(\sigma (x)=\prod\limits_{i=1}^k \sum\limits_{j=0}^{c_i} p_i^j\)。其中 \(\sigma (x)\) 表示 \(x\) 的約數和。

\(f_i\) 表示 \(i\) 的約數和,\(g_i\) 表示 \(1+p+p^2+\cdots +p^c\),其中 \(p\) 表示 \(i\) 的最小質因子。

g[1]=f[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) p[++cnt]=i,g[i]=f[i]=i+1;    //當 i 是質數時,顯然有 g[i]=f[i]=i+1
    for(int j=1;j<=cnt&&i*p[j]<=n;j++){
        vis[i*p[j]]=1;
        int x=i*p[j];
        if(i%p[j]==0){
            g[x]=g[i]*p[j]+1,f[x]=f[i]/g[i]*g[x];    //多了一個因子,也就是 1+p^1+p^2+...+p^c 變成了 1+p^1+p^2+...+p^{c+1} 了,那么更新 g 只需要將所有的乘上 p 再 +1 就好了。 
            break;
        }
        else f[x]=f[i]*f[p[j]],g[x]=1+p[j];    //新的因子(i*p[j] 里原先沒有 p[j] 這一項) 。p[j] 是 i*p[j] 的最小質因子。 
    }
}
for(int i=1;i<=n;i++) f[i]=f[i-1]+f[i];

2. 求歐拉函數

對於正整數 \(n\),歐拉函數是小於或等於 \(n\) 的正整數中與 \(n\) 互質的數的數目,記作 \(\varphi(n)\)

\(\varphi(n)=\sum_{i=1}^n \ [\gcd(i,n)=1]\)

\([a]\):如果 \(a\) 為真,則 \([a]\) 的值為 \(1\);否則為 \(0\)

\(n=\prod_{i=1}^k{p_i}^{c_i}\)\(p_i\) 是質數)。

公式:\(\varphi(n)=n\prod_{i=1}^k(1-\frac{1}{p_i})\)

vis[0]=vis[1]=1,phi[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) p[++cnt]=i,phi[i]=i-1;    //當 i 為質數時,顯然有 phi[i]=i-1。 
    for(int j=1;j<=cnt&&i*p[j]<=n;j++){
        vis[i*p[j]]=1;
        if(i%p[j]==0){
            phi[i*p[j]]=phi[i]*p[j];    //i 和 i*p[j] 都包含 p[j] 這個質因子。將 phi[i*p[j]] 和 phi[i] 按照公式寫出,二者相除,商為 p[j]。 
            break;
        }
        phi[i*p[j]]=phi[i]*phi[p[j]];    //i 和 p[j] 互質。利用積性函數的性質。 
    }
}

3. 求莫比烏斯函數

\(p_i\) 是質數。\(\mu (n)=\begin{cases}1&{n=1}\\(-1)^k&c_{1,2,...,k}=1\ (n=\prod_{i=1}^k p_i^{c_i})\\0&c_i>1\end{cases}\)

證明莫比烏斯函數是積性函數:

對於 \(\forall a,b\),並且 \(\gcd(a,b)=1,ab=x\)\(\mu(a)\mu(b)\)

1. 若 \(\mu(a)=0\)\(\mu(b)=0\),則 \(ab\) 必含平方因子(即存在 \(c_i>1\))。所以 \(\mu(a,b)=0\)

2. 否則,由於 \(\gcd(a,b)=1\),則 \(ab\) 不含平方因子。則 \(\mu(a)\mu(b)=(-1)^{k_1}(-1)^{k_2}=(-1)^{k_1+k_2}=\mu(ab)\)

vis[0]=vis[1]=1,u[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) p[++cnt]=i,u[i]=-1;    //當 i 為質數時,此時 k=1 且 c1=1,顯然有 u[i]=-1 
    for(int j=1;j<=cnt&&i*p[j]<=n;j++){
        vis[i*p[j]]=1;
        if(i%p[j]==0){u[i*p[j]]=0;break;}    //多了一個因子。此時 i*p[j] 必含平方因子,所以 u[i*p[j]]=0。 
        u[i*p[j]]=-u[i];    //新的因子。 
    }
}

4. 求約數個數

根據唯一分解定理,可得:\(n=p_1^{c_1}\times p_2^{c_2}\times \cdots \times p_k^{c_k}=\prod\limits_{i=1}^kp_i^{c_i}\)

\(p_i^{c_i}\) 的約數有 \(p_i^0,p_i^1,\cdots,p_i^{c_i}\)\(c_i+1\) 個,根據乘法原理,可得 \(n\) 的約數個數為 \(d(n)=\prod\limits_{i=1}^k (c_i+1)\)

\(d_i\) 表示 \(i\) 的約數個數,\(g_i\) 表示 \(i\) 的最小質因數的次數。

vis[0]=vis[1]=1,d[1]=1;
for(int i=2;i<=n;i++){
    if(!vis[i]) p[++cnt]=i,d[i]=2,g[i]=1;    //若 i 為質數,顯然有 d[i]=2,g[i]=1 
    for(int j=1;j<=cnt&&i*p[j]<=n;j++){
        vis[i*p[j]]=1;
        if(i%p[j]==0){
            d[i*p[j]]=d[i]/(g[i]+1)*(g[i]+2);    //多了一個因子。並且 p[j] 是 i*p[j] 的最小質因子。d[i*p[j]]=(1+c1+1)(1+c2)...(1+ck)。 
            g[i*p[j]]=g[i]+1; 
            break;
        }
        d[i*p[j]]=d[i]*d[p[j]],g[i*p[j]]=1;    //新的因子。i*p[j] 之前不包含 p[j]。p[j] 是 i*p[j] 的最小質因子。 
    }
}

四、一般線性篩

一個積性函數 \(f\),可以考慮以下的線性篩法:

  • 要計算 \(f(n)\),考慮到 \(n=\prod\limits_{i=1}^k p_i^{c_i}\)

  • 在線性篩的過程中,如果 \(i\bmod p[j]\neq 0\),就有 \(f(i\cdot p[j])=f(i)f(p[j])\)

  • 否則,考慮 \(k\mid (i\cdot p[j]),\gcd(k,p[j])=1\),則有 \(f(i\cdot p[j])=f(k)f(\frac{i\cdot p[j]}{k})\)

  • 因此,我們只需要記錄對於每一個 \(i\cdot p[j]\) 對應的 \(k\),或者推導出 \(f(p^c)\)\(f(p^{c+1})\) 的關系式即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM