線性篩(尤拉篩)

2021-08-04 22:54:22 字數 3413 閱讀 2077

昨天的考試跪的一塌糊塗:第一題水過,第二題帶wa的樸素,最後題忘了特判左端點全跪,分數比起預計得分整整打了個對折啊!

步入正題:線性篩(尤拉篩)

一般的篩法(ppt裡叫埃拉託斯特尼篩法,名字異常高貴)的效率是o(nlglgn)(其實很接近o(n)啊!),對於一些例如n=10000000的殘暴資料會跪,於是,線性篩登場了…

1 #include 2 using namespace std;

3 int prime[1100000],primesize,phi[11000000];

4 bool isprime[11000000];

5 void getlist(int listsize)

6 17 }

18 }

以上是線性篩**。

就我的理解,線性篩有兩個地方與一般篩不同:

1.兩層迴圈的順序不同(一般篩是第一維prime[i] 第二維j,尤拉篩是第一維i 第二位prime[j])

2.一行神奇的**:

14             if(i%prime[j]==0)break;
這行**神奇地保證了每個合數只會被它的最小素因子篩掉,就把複雜度降到了o(n)。

接下來是證明這個演算法正確性的說明:

prime陣列中的素數是遞增的,當i能整除prime[j],那麼i*prime[j+1]這個合數肯定被prime[j]乘以某個數篩掉。

因為i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],那麼i*prime[j+1]=(k*prime[j])*prime

[j+1]=k』*prime[j],接下去的素數同理。所以不用篩下去了。因此,在滿足i%prime[j]==0這個條件之前以及第一次

滿足改條件時,prime[j]必定是prime[j]*i的最小因子。

之前看了網上各種各樣的說法,整個理解都有問題,總算是明白了。

顯然,線性篩只拿來篩篩素數是很不科學的,它的速度大約是一般篩的3~4倍,在資料量小的時候甚至慢些(用到了mod運算)。

接下來就是它的重量級應用了:求解積性函式

p.s.睡了一覺,精神抖擻!

積性函式f(x)應滿足f(a×b)=f(a)×f(b),a與b應互素。而完全積性函式則應滿足對於任意的a與b,前面的等式應成立。

先是尤拉phi函式(反演不怎麼懂):

1 void getlist(int listsize)

2 12 for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++)

13

20 phi[i*prime[j]]=phi[i]*(prime[j]-1);

21 }

22 }

23 }

至於為什麼這樣的遞推是成立的,有如下的幾個原因:

1.每個合數只會被篩到一次(前面說明過)。

2.當正整數p是素數時,phi[p]=p-1。

3.phi函式是乙個積性函式,當a與b互素時,滿足phi(a×b)=phi(a)×phi(b)。

4.上述**中的i永遠大於等於prime[j],又因為prime[j]必然是素數,所以i、prime[j]互素當且僅當i=0 (mod prime[j])。

5.當p是素數時,phi(pk)=(p-1)×pk-1

原因1保證了每個數的phi只會被計算一次,第10行的**可以由原因2解釋,第20行的**可以由原因2與原因3解釋,第17行的**可以由原因5所解釋。

還是覺得這個演算法好神奇……

接下來的東西就和昨天的考試有關了,先是求每個數的因數個數:

記每個數因數個數為e(i),顯然函式e(i)是積性函式但不是完全積性函式。

首先,當p是素數時e(i)=2。

對於i與prime[j]互素的情況,直接相乘即可。而對於i與prime[j]不互素的情況,就有點複雜了。

我們要再記錄乙個num陣列,num[i]表示i的最小素因子(即每次遞推時的prime[j])的次數。

對於i與prime[j]不互素的情況,e(i×prime[j])=e(i)÷(num[i]+1)×(num[i]+2)。

num陣列的維護也是很方便的:對於所有n為素數或n=i×prime[j](i與prime[j]互素)的情況,num(i)=1,對於另外一種情況num[i*prime[j]]=num[i]+1。

再是求因數和還有因數平方和(兩者差不了多少)

設數n的因數和為q(n),有q(n)=(1+p1+p1

2+…+p1

k1)×(1+p2+p2

2+…+p2

k2)×…×(1+pn+pn

2+…+pn

kn)。

顯然,函式q是積性的,我們只用考慮i與prime[j]不互質的情況,一種辦法是用乘法逆元搞,顯然這是不科學的,真正的做法如下

q(i×prime[j])=q(i÷prime[j]num[i])×q(prime[j]num[i]+1)

其中的prime[j]num[i]是可以預先存好的。這種做法還是利用了積性函式的性質。

最後附上昨天第二題的**

1 #include 2 #include 3 using namespace std;

4 inline long long imax(long long a,long long b)

5 const int mo=1000000007;

6 long long q[2100000];

7 long long prime[11000000],pnum,ynum[11000000],ysum[11000000],c[11000000];

8 bool isprime[10000001];

9 void getlist(int listsize)

10 22 for(int j=1;j<=pnum&&i*prime[j]<=listsize;j++)

23

34 else

35

39 break;

40 }

41 else

42

47 }

48 }

49 }

50 int main(int argc, char *argv)

51 62 getlist(biggest);

63 long long ansnum=0,anssum=0;

64 for(int i=1;i<=n;i++)

65

73 }

74 cout<

尤拉篩 線性篩

實現 include using namespace std const int max n 1e8 int prime max n cnt bool st max n 使用bool陣列節省空間 void is prime int n intmain 每個合數只被自己最小的質因子篩去。現在證明在i ...

尤拉篩(線性篩)

尤拉篩多用於篩素數,時間複雜度是o n 主要原理 合數 最小質因子 合數 質數 這個合數的組成是唯一的,尤拉篩裡面只在這種情況篩一次,也就是每個數就篩一次,可以完成o n 的複雜度。每當列舉到乙個數,把這個數當作後面的那個數,在已經得到的質數里列舉當作最小質因子,看看這樣的組合能找到哪個合數,但是要...

線性篩法(尤拉篩)

從前有乙個素數篩法叫埃拉託斯特尼篩法,它的思想很簡單,把1 n以內素數的整數倍的數字劃掉,留下的就全是素數,但是它的複雜度是o nlglgn 對於大量不友好資料會跪,於是線性曬登場了。include using namespace std int prime 1100000 primesize,ph...