1. 程式人生 > >尤拉篩(線性篩)& 尤拉函式

尤拉篩(線性篩)& 尤拉函式

今天又複習了一下尤拉篩法,在這做個筆記。

尤拉篩(線性篩)

一般情況下,有一種篩法叫埃什麼什麼的。是O(nloglogn),非常接近於O(n),但也會有坑爹的出題人來個10000000故意卡你。

原理

這可能原理有點妙啊。

  • pr[i]為i最小質因子,然後從2開始計算
  • 如果pr[i]沒有在前面得到,就說明i是質數,所以pr[i]=i,prime[++len]=i
  • 對於i,列舉每一個不超過pr[i]的質數prime[j],所以pr[iprime[j]]=prime[j]

這樣可以發現,每一個數只會被它最小的質因子篩去,所以保證了演算法為O(n)的。

我們見一下程式碼:

程式碼

void
find_prime(int n) { memset(isprime,0,sizeof(isprime)); sp = 0; for(int i = 2;i <= n;i++) { if(!isprime[i]) { prime[++sp] = i; isprime[i] = i; } for(int j = 1;j <= sp && i * prime[j] <= n;j++) { isprime[i*prime[j]] = prime[j]; if
(prime[j] >= isprime[i]) break;//用isprime[i]來表示i的最小質因子,可能用mod比較慢 } } }

在break那一行,有的人會用
      if(i % prime[j]==0) break;
不過也可以通過isprime[i]儲存i最小的質因子來與prime[j]做比較。
但不管怎麼寫,我人認為最重要的就是含break的那個語句

尤拉函式

不過,僅僅篩素數就顯得線性篩不是那麼必要。
線性篩最科學的戰場——求積性函式
作為蒟蒻,我以尤拉函式來舉例:

程式碼

先粗淺地看一下程式碼

void find_prime(int
n) { memset(isprime,0,sizeof(isprime)); sp = 0; for(int i = 2;i <= n;i++) { if(!isprime[i]) { prime[++sp] = i; isprime[i] = i; phi[i] = i-1;//① } for(int j = 1;j <= sp && i * prime[j] <= n;j++) { isprime[i*prime[j]] = prime[j]; if(prime[j] >= isprime[i]) { phi[i*prime[j]] = phi[i]*prime[j];//② break; } phi[i*prime[j]] = phi[i]*(prime[j]-1);//③ } } }

原理

  1. 每個數字只會被篩到一次
  2. 當正整數p為素數時,phi[p]=p1
  3. 尤拉函式既然是積性函式,就說明當a與b互質時,滿足phi(ab)=phi(a)phi(b)
  4. 當p為素數時, phi(pk)=(p1)pk1
  5. 我們保證了每次的prime[j]小於等於i的最小質因子,所以當prime[j]<isprime[i]時,phi[iprime[i]]=phi[i]phi[j]=phi[i](j1),而在i%prime[j]==0時,直接乘上prim[j]

上述的五點:1表明每個數值被算一次。2解釋了①。3、4、5解釋了②和③。