生活随笔
收集整理的這篇文章主要介紹了
刘汝佳训练指南——数论专题知识点总结:
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
數論是一個神奇的東西,各種結論都很經典,有些懂,有些自己還不是很懂。
接下來就一個一個的介紹吧。
第一、素數,素數本身就是一個很讓人驚奇的數,因為它代表的是唯一,自己就有連個因數,一個是1,一個是自己,因為1是每個數都具備的因子(除了0),所以,它也就相當于只有自己。是一個自我感覺很良好的人呀!
最樸素的算法當然是從2-sqrt(2)里面找因子,如果沒有,就說明它是個素數。為什么到sqrt(n)?你想,sqrt(n)* sqrt(n)= n,而你因子的個數怎么算?是不是從1-sqrt(n)里面的因子數 * 2?顯然可得,因子數是關于sqrt(n)對稱的,這邊有幾個那邊就有幾個,所以枚舉一般就行!
高深一點,有miller-robin,前面寫過這個算法,但是好像不經常用,也就沒去看過了。但是有一個是比較經常用的,那就是篩法欲求范圍素數。這個思想運用廣泛,euler函數里面也用到了這個思想。
貼出篩素數的算法,一會加上miller-robin:
[cpp]?view plaincopy
#include?<stdio.h>??#include?<string.h>??#include?<math.h>??#include?<iostream>??#include?<string>????using?namespace?std;????const?int?MAXN?=?1000000?+?11;????bool?p[MAXN];????int?index[MAXN];????void?init()??{??????memset(p,?0,?sizeof(p));??????p[0]?=?1;??????p[1]?=?1;??????for?(int?i?=?4;?i?<?MAXN;?i?+=?2)??????{??????????p[i]?=?1;??????}??????int?cnt?=?0;??????index[cnt++]?=?2;???????for?(int?i?=?3;?i?<?(int)sqrt(MAXN?*?1.0);?i?+=?2)??????{??????????if?(!p[i])??????????{??????????????index[cnt++]?=?i;??????????????int?k?=?i?*?2;??????????????for?(int?j?=?k;?j?<?MAXN;?j?+=?i)??????????????{??????????????????p[j]?=?1;??????????????}??????????}??????}??????/*?????for?(int?i?=?0;?i?<?cnt;?i++)?????{?????????printf("%d\n",?index[i]);?????}?????*/??}????void?sieve()//這是另外一種,這種看上去簡單,但是和上面的思想是一摸一樣的.???{??????int?m?=?(int)sqrt(n?+?0.5);??????for?(int?i?=?2;?i?<?m;?i++)??????{??????????if?(!p[i])??????????{??????????????for?(int?j?=?i?*?i;?j?<?n;?j?+=?i)??????????????{??????????????????p[j]?=?1;??????????????}??????????}??????}??}????int?main()??{??????init();??????sieve();??????system("pause");??????return?0;??}??二、歐幾里德算法。歐幾里德算法的精髓思想大概是輾轉相除吧。還是比較好理解的,難點的就是擴展歐幾里德算法,求a*x + b*y = gcd(a,b);這是一個解線性方程組的最佳算法。還有一個很好的應用就是求乘法逆元,這個應用很大,因為有很多時候因為數據量非常大,都會modulo一個素數。而逆元可以把除以一個數變成乘法,這個就比較好了。貼出這些算法:
[cpp]?view plaincopy
#include?<stdio.h>??#include?<string.h>??#include?<iostream>??#include?<string>????using?namespace?std;????typedef?long?long?LL;????void?extgcd(LL?a,?LL?b,?LL?&d,?LL?&x,?LL?&y)??{??????if?(b?==?0)??????{??????????x?=?1;??????????y?=?0;??????????d?=?a;??????}??????else??????{??????????extgcd(b,?a?%?b,?d,?y,?x);??????????y?-=?x?*?(a?/?b);??????}????????????}????LL?inv(LL?a,?LL?n)??{??????LL?d,?x,?y;??????extgcd(a,?n,?d,?x,?y);??????return?d?==?1???(x?+?n)?%?n?:?-1;??}????int?main()??{??????int?ans?=?inv(2,?5);??????cout?<<?ans?<<?endl;??????system("pause");??????return?0;??}??三、歐拉函數phi(n)。首先就要弄明白歐拉函數求得的含義:1-n之間和n互素的數的個數。公式是phi(n) = n * (1 - 1 / p1) (1 - 1 / p2) (1- 1 / p3) (1 - 1 / p4)……
p1,p2,p3都是n素因數分解的素因數。有了這個公式就好辦了。
現在貼出素因數分解的代碼,其實在euler_phi函數里面就包含了這個思想,就是含有這個因子就把這個因子除盡。
素因數分解:
[cpp]?view plaincopy
#include?<stdio.h>??#include?<string.h>??#include?<math.h>??#include?<iostream>??#include?<string>????using?namespace?std;????int?main()??{??????int?N;??????while?(scanf("%d",?&N)?!=?EOF)??????{??????????int?cnt?=?0;??????????cout?<<?"N?=?";??????????for?(int?i?=?2;?i?<=?(int)sqrt(N?+?0.5);?i++)??????????{??????????????if?(N?%?i?==?0)??????????????{??????????????????cout?<<?i?<<?"^";??????????????????while?(N?%?i?==?0)??????????????????{??????????????????????N?/=?i;??????????????????????cnt++;??????????????????}???????????????????cout?<<?cnt?<<?"?";??????????????}??????????}??????????if?(N?>?1)??????????{??????????????cout?<<?N?<<?"^1";??????????}??????????cout?<<?endl;??????}??????system("pause");??????return?0;??}???接下來是euler_phi:
[cpp]?view plaincopy
#include?<stdio.h>??#include?<string.h>??#include?<math.h>??#include?<iostream>??#include?<string>??/*??*首先你得清楚,歐拉函數的公式是什么:??*推導出來的最簡潔的公式是:phi(n)?=?n(1?-?1/p1)(1?-?1/p2)……??*這就可以輕松的求出來了???*phi(n)?表示的含義是,不超過x且和x互素的整數個數.??*/????using?namespace?std;????typedef?long?long?LL;????const?int?MAXN?=?100000?+?11;????int?phi[MAXN];????int?euler_phi(int?n)??{??????LL?ans?=?n;??????for?(int?i?=?2;?i?<=?(int)sqrt(n?+?0.5);?i++)??????{??????????if?(n?%?i?==?0)??????????{??????????????ans?=?ans?/?i?*?(i?-?1);??????????????while?(n?%?i?==?0)??????????????{??????????????????n?/=?i;??????????????}??????????}??????}??????if?(n?>?1)??????{??????????ans?=?ans?/?n?*?(n?-?1);??????}??????return?ans;??}????void?phi_table(int?n)??{??????memset(phi,?0,?sizeof(phi));??????phi[1]?=?1;??????for?(int?i?=?2;?i?<=?n;?i++)?//因為要將所有的phi都求出來,所以要循環到n,因為有一些大于sqrt(n)???????{????????????????????????????//的素數還沒有求出結果;???????????if?(!phi[i])??????????{??????????????for?(int?j?=?i;?j?<?n;?j?+=?i)??????????????{??????????????????if?(!phi[j])??????????????????{??????????????????????phi[j]?=?j;??????????????????}??????????????????phi[j]?=?phi[j]?/?i?*?(i?-?1);??????????????}??????????}??????}??}????void?print(int?n)??{??????for?(int?i?=?1;?i?<=?n;?i++)??????{??????????printf("phi[%d]?=?%d\n",?i,?phi[i]);??????}??}?????int?main()??{??//??cout?<<?"phi(i)?=?"?<<?euler_phi(3)?<<?endl;??????phi_table(30);??????print(30);??????system("pause");??????return?0;??}??這只是最基本的算法,當然數論里面還有很多種算法,我會后續加上來的。
四、中國剩余定理。解決多個模方程,但是變量還是一個的問題,即x = a[i] (% m[i])。方法是令M為所有的m[i]的乘積,wi = M / mi,則gcd(wi, mi) = 1.使得wi * p + mi * q = 1,可以用extgcd求出來對于wi的p解,令e = ?wi * pi,則方程組等價于方程x = e1*a1 + e2*a2 + e3*a3…… 且注意x是唯一解。
代碼如下:
[cpp]?view plaincopy
#include?<stdio.h>??#include?<string.h>??#include?<iostream>??#include?<string>??/*??*中國剩余定理用與解決?x?=?a[i]?(%?m[i]);??*而m[i]又每每互素,將會求的唯一的最小解。???*/?????using?namespace?std;????typedef?long?long?LL;????const?int?MOD?=?1000000000?+?7;????void?extgcd(int?a,?int?b,?int?&d,?int?&x,?int?&y)??{??????if?(b?==?0)??????{??????????d?=?a;??????????x?=?1;??????????y?=?0;??????}??????else??????{??????????extgcd(b,?a?%?b,?d,?y,?x);??????????y?-=?x?*?(a/?b);??????}??}????int?china(int?n,?int?*a,?int?*m)??{??????int?M?=?0;??????int?x,?y,?d;??????int?ans?=?0;??????for?(int?i?=?0;?i?<?n;?i++)??????{??????????M?+=?m[i];??????}??????for?(int?i?=?0;?i?<?n;?i++)??????{??????????int?w?=?M?/?m[i];??????????extgcd(m[i],?w,?d,?x,?y);??????????ans?=?((LL)ans?+?(LL)y?*?w?*?a[i])?%?MOD;??????}??????return?(ans?+?MOD)?%?MOD;??}????int?main()??{??????system("pause");??????return?0;??} ?
總結
以上是生活随笔為你收集整理的刘汝佳训练指南——数论专题知识点总结:的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。