数论入门知识
數(shù)論入門(mén)
提到數(shù)論,可能很多人都感到很頭疼,甚至很多時(shí)候遇到一些問(wèn)題,看到成篇的證明都會(huì)感到恐懼,而且由于關(guān)于ACM方面的數(shù)論資料,網(wǎng)上資料都比較駁雜。有時(shí)候很容易出現(xiàn)知其然不知其所以然的情況。所以今天給大家介紹一些關(guān)于數(shù)論入門(mén)最基礎(chǔ)的知識(shí)和算法,內(nèi)容會(huì)盡量從0基礎(chǔ)開(kāi)始,所以內(nèi)容會(huì)盡量詳細(xì)。不過(guò)其中部分證明還是需要一些高中數(shù)學(xué)基礎(chǔ)的。由于內(nèi)容很多,我整理了一個(gè)目錄,按順序講今天的內(nèi)容.
目錄
一.合數(shù),質(zhì)數(shù),整除,互質(zhì),同余,取模等基礎(chǔ)概念。
二.歐幾里得算法
三.擴(kuò)展歐幾里得
四.費(fèi)馬小定理
五.歐拉函數(shù)
六.歐拉降冪
七.素?cái)?shù)篩
八.快速冪
九.逆元的用處和幾種簡(jiǎn)單求法
十.中國(guó)剩余定理
一.數(shù)論中的基礎(chǔ)概念和符號(hào)
1.基礎(chǔ)概念
合數(shù):合數(shù)是指在大于1的整數(shù)中除了能被1和本身整除外,還能被其他數(shù)(0除外)整除的數(shù)
質(zhì)(素)數(shù):質(zhì)(素)數(shù)是指在大于1的自然數(shù)中,除了1和它本身以外不再有其他因數(shù)的自然數(shù)。
整除:若整數(shù)b除以非零整數(shù)a,商為整數(shù),且余數(shù)為零, 我們就說(shuō)b能被a整除(或說(shuō)a能整除b),b為被除數(shù),a為除數(shù),即a|b(“|”是整除符號(hào)),讀作“a整除b”或“b能被a整除”。a叫做b的約數(shù)(或因數(shù)),b叫做a的倍數(shù)。整除屬于除盡的一種特殊情況。
公約數(shù):公約數(shù),亦稱“公因數(shù)”。它是指能同時(shí)整除幾個(gè)整數(shù)的數(shù) [1] 。如果一個(gè)整數(shù)同時(shí)是幾個(gè)整數(shù)的約數(shù),稱這個(gè)整數(shù)為它們的“公約數(shù)”;公約數(shù)中最大的稱為最大公約數(shù)。對(duì)任意的若干個(gè)正整數(shù),1總是它們的公因數(shù)。
互質(zhì):互質(zhì)是公約數(shù)只有1的兩個(gè)整數(shù),叫做互質(zhì)整數(shù)
同余:數(shù)論中的重要概念。給定一個(gè)正整數(shù)m,如果兩個(gè)整數(shù)a和b滿足a-b能夠被m整除,即(a-b)/m得到一個(gè)整數(shù),那么就稱整數(shù)a與b對(duì)模m同余,記作a≡b(mod m)。對(duì)模m同余是整數(shù)的一個(gè)等價(jià)關(guān)系
2.常見(jiàn)符號(hào)
MOD
mod,要與一般的%相區(qū)分
mod意為模意義下結(jié)果一定為正
%是一種運(yùn)算,結(jié)果可以為負(fù)
同余符號(hào)(≡)
兩個(gè)整數(shù)a,b,如果a mod m = b mod m則稱a,b對(duì)于模m同余
記作a ≡ b ( mod m )
sigma(Σ )
求和符號(hào)
∑i=1ni\sum_{i=1}^{n}{i} i=1∑n?i
sigma的意思是i取值1(下界)到n(上界)后面的表達(dá)式的和,這個(gè)公式里的值是1 + 2 + 3 + ? ? ? + ( n ? 1 ) + n 1+2+3+···+(n-1)+n1+2+3+???+(n?1)+n
求積符號(hào)Π
∏i=1ni\prod_{i=1}^{n}{i} i=1∏n?i
求積符號(hào),這個(gè)公式代表的就是n的階乘
同余≡
兩個(gè)整數(shù)a,b,若它們除以整數(shù)m所得的余數(shù)相等,則稱a,b對(duì)于模m同余
記作 a ≡ b (mod m) 讀作a同余于b模m,或讀作a與b關(guān)于模m同余。
比如 26 ≡ 14 (mod 12)
μ莫比烏斯函數(shù)
代表了莫比烏斯函數(shù)。
φ歐拉函數(shù)
在數(shù)論中代表歐拉函數(shù)
定義:小于n的正整數(shù)中與n互質(zhì)的數(shù)的數(shù)目
|整除符號(hào)
整除符號(hào):x|y,表示x整除y,即x是y的因數(shù)
gcd(x,y)
代表x和y的最大公約數(shù),也可寫(xiě)作(x,y)
lcm(x,y)
代表x和y的最小公倍數(shù),也可寫(xiě)作[X,Y];
二.歐幾里得算法
再說(shuō)歐幾里得算法之前,我們不得不提到最大公約數(shù)( Greatest Common Divisor)一般縮寫(xiě)為gcd,這歐幾里得算法,就是歐幾里得發(fā)明的用來(lái)求兩個(gè)數(shù)最大公約數(shù)的算法,也稱輾轉(zhuǎn)相除法。它的公式是
gcd(a,b)=gcd(b,amodb)gcd(a,b) = gcd(b,a mod b) gcd(a,b)=gcd(b,amodb)
它的證明也有很多種方法,這里我們只介紹一種基礎(chǔ)的證法
證明了這個(gè)公式,我們想要算出gcd(a,b)的結(jié)果也就很簡(jiǎn)單了。這兩個(gè)數(shù)的大家都會(huì)不斷減小,我們只需要遞歸不斷縮小范圍低軌道b==0的時(shí)候就能算出a和b的最大公約數(shù)了,如果gcd(a,b)==1,說(shuō)明a和b互質(zhì)。
int gcd(int a, int b) {if (b == 0) return a;return gcd(b, a % b); } int gcd(int a , int b){return a%b==0? b:gcd(b,a%b); }它的時(shí)間復(fù)雜度是O(log n);其實(shí)很好理解,在遞歸求gcd的過(guò)程中,如果a>b程序會(huì)返回gcd(b,a)如果a>b。gcd(a,b)-=gcd(b,a mod b)取模后a的值至少會(huì)折半,所以這個(gè)過(guò)程只會(huì)發(fā)生logn次。而每次第一種情況后一定會(huì)轉(zhuǎn)移到第二種情況,第一種情況的次數(shù)不大于第二種情況,所以這個(gè)算法的總復(fù)雜度是O(log n)級(jí)別.
介紹完了最大公約數(shù),不得不提的就是最小公倍數(shù)(Least Common Multiple)lcm了。
這里也有一個(gè)公式可以直接算,就是gcd(a,b)*lcm(a,b)=a * b
如何證明呢?
gcd(a,b)=t假設(shè)a=k1?t,b=k2?t易得gcd(k1,k2)=1lcm(a,b)=k1?k2?t可以發(fā)現(xiàn)gcd(a,b)?lcm(a,b)=a?bgcd(a,b)=t\\ 假設(shè)a =k1*t,b=k2*t\\ 易得gcd(k1,k2)=1\\ lcm(a,b)=k1*k2*t\\ 可以發(fā)現(xiàn)gcd(a,b)*lcm(a,b)=a*b\\ gcd(a,b)=t假設(shè)a=k1?t,b=k2?t易得gcd(k1,k2)=1lcm(a,b)=k1?k2?t可以發(fā)現(xiàn)gcd(a,b)?lcm(a,b)=a?b
需要注意的是,如果在代碼中使用,不要直接寫(xiě)成(a*b)/gcd(a,b)而是先算a/gcd(a,b)在乘b,放置超出數(shù)據(jù)范圍。
三.擴(kuò)展歐幾里得算法(exgcd)
裴蜀定理
在講擴(kuò)展歐幾里得之前,我們先介紹一下一個(gè)定理,裴蜀定理
裴蜀定理,又稱貝祖定理(Bézout’s lemma)。是一個(gè)關(guān)于最大公約數(shù)的定理。
其內(nèi)容是:設(shè)a,b是不全為零的整數(shù),則存在整數(shù)x,y , 使得ax+by=gcd(a,b) .
證明就不詳細(xì)展開(kāi)了,有興趣可以上網(wǎng)查一查,其實(shí)思想和輾轉(zhuǎn)相除法差別不大。
擴(kuò)展歐幾里得定理
擴(kuò)展歐幾里得算法實(shí)際上就是對(duì)于ax+by=gcd(a,b),一定有一組整數(shù)解x,y使其成立
對(duì)于這個(gè)式子的證明,可以采用數(shù)學(xué)歸納法進(jìn)行實(shí)現(xiàn),先證明當(dāng)n= 1時(shí)命題成立。
假設(shè)n=m時(shí)命題成立,那么可以推導(dǎo)出在n=m+1時(shí)命題也成立。(m代表任意自然數(shù))
然后命題得證。
關(guān)于它的代碼
inline int exgcd(int a, int b, int &x, int &y) {if(b == 0){x = 1, y = 0, return a;}int d = exgcd(b, a % b, x, y);int z = x;x = y, y = z - y * (a / b);return d; }通過(guò)他我們可以的到方程的一組特解。
知道了特解之后,我們可以通過(guò)他求出方程的所有解
x=x0+kbdy=y0?kadx=x_0+k\frac{b}ozvdkddzhkzd\\ y=y_0-k\frac{a}ozvdkddzhkzd\\ x=x0?+kdb?y=y0??kda?
有了這個(gè)式子。我們就可以求出任意方程ax + by = c , gcd ? ( a , b ) ∣ c的所有解為
x=cdx0+kbd.y=cdy0?kadx=\frac{c}ozvdkddzhkzdx_0+k\frac{b}ozvdkddzhkzd\\ .\\ y=\frac{c}ozvdkddzhkzdy_0-k\frac{a}ozvdkddzhkzd\\ x=dc?x0?+kdb?.y=dc?y0??kda?
關(guān)于擴(kuò)展歐幾里得的證明和應(yīng)用,這里我們先講這么多,在后面講逆元的過(guò)程中,還有用到的機(jī)會(huì)
四.費(fèi)馬小定理
若p為質(zhì)數(shù),且gcd(a,p)=1,那么就有:
ap?1≡1(modp)a^{p-1} \equiv 1 \quad (mod \quad p) ap?1≡1(modp)
打公式怎么看格式都不好看,還是用紙來(lái)寫(xiě)吧。
這是一種很巧妙的費(fèi)馬小定理證法。但實(shí)際上正統(tǒng)的證明方法是引入了完全剩余系和縮剩余系。這些概念在代數(shù)系統(tǒng)中式很重要的,不過(guò)今天的目的知識(shí)給大家見(jiàn)簡(jiǎn)單介紹數(shù)論的基礎(chǔ),暫時(shí)不深入展開(kāi)來(lái)講了。實(shí)際上歐拉定理和費(fèi)馬小定理都是拉格朗日定理的一個(gè)推論。對(duì)于一個(gè)有限群,元素的階一定式群階數(shù)的因子。感興趣的同學(xué)可以在網(wǎng)上查一查相關(guān)證明。這里不多贅述
證明完了以后,我們來(lái)看看他有什么用
實(shí)際上,費(fèi)馬小定理的應(yīng)用場(chǎng)景還是很廣泛的,這里我們只從競(jìng)賽的角度說(shuō)幾個(gè)它的簡(jiǎn)單應(yīng)用。
1.它可以用來(lái)判斷大質(zhì)數(shù)也就式Miller-Rabin質(zhì)數(shù)判定
2.它可以用來(lái)進(jìn)行費(fèi)馬小定理降冪也就是
ak≡akmod(p?1)modpa^k\equiv a^{k\quad mod\quad(p-1)}mod \quad p ak≡akmod(p?1)modp
3.在后文求解逆元的過(guò)程中,我們也會(huì)用到費(fèi)馬小定理
五.歐拉定理
我們直接來(lái)看歐拉定理
?a,m,若gcd(a,m)=1,則有:aφ(m)≡1(modm)\forall a,m, 若 gcd(a,m) = 1 ,則有: a^{\varphi(m)} \equiv 1 \quad (mod \quad m) ?a,m,若gcd(a,m)=1,則有:aφ(m)≡1(modm)
到了這里我們其實(shí)可以發(fā)現(xiàn),費(fèi)馬小定理就是歐拉定理的在m為素?cái)?shù)下的一種特殊情況。通過(guò)這個(gè)定理在一些特殊情況下求解逆元很好用。并且有一些題目會(huì)考察對(duì)它的理解,在數(shù)論中歐拉定理的應(yīng)用面非常廣泛。是一個(gè)非常重要的定理。不過(guò)涉及競(jìng)賽的一般用的最多就是歐拉降冪公式。我們下面也會(huì)提到。這里的證明方法只是幫助大家理解他的本質(zhì)。如果不理解,在競(jìng)賽中,記住歐拉降冪公式和應(yīng)用就夠了。不過(guò)理解這些公式的本質(zhì)在所以寫(xiě)考察思想的題目中還是有所幫助的。
六.歐拉降冪(擴(kuò)展歐拉定理)
若a和m互質(zhì)
aK≡akmodφ(m)(modm)a^K \equiv a^{k \ mod \ \varphi(m)} \quad (mod \quad m) aK≡ak?mod?φ(m)(modm)
若a和m不互質(zhì)則有
?k>φ(m),有ak≡akmodφ(m)+φ(m)(modm)\forall k > \varphi(m),有 a^k \equiv a^{k \ mod \ \varphi(m) + \varphi(m)} \quad (mod \quad m) ?k>φ(m),有ak≡ak?mod?φ(m)+φ(m)(modm)
證明過(guò)于繁雜,會(huì)用實(shí)現(xiàn)降冪就行、
在一些計(jì)數(shù)的問(wèn)題中,常常要求對(duì)結(jié)果取模,但是在計(jì)算非常龐大的次冪的時(shí)候,無(wú)法直接取模,可以先把底數(shù)對(duì) p 取模,指數(shù)對(duì) φ ( p )取模,再計(jì)算次冪,有效地降低時(shí)間復(fù)雜度。
這里給除一個(gè)例題幫助大家理解它的用法
計(jì)算abmodm,1<=a<=109,1<=b<=1020000000,1<=m<=108計(jì)算a^b mod\quad m,1<=a<=10^9,1<=b<=10^{20000000},1<=m<=10^8 計(jì)算abmodm,1<=a<=109,1<=b<=1020000000,1<=m<=108
七.線性篩
1.Miller-Rabin 素?cái)?shù)測(cè)試
有時(shí)候我們想快速的知道一個(gè)數(shù)是不是素?cái)?shù),但是數(shù)太大這時(shí)候我們可以對(duì)其進(jìn)行 Miller-Rabin 素?cái)?shù)測(cè)試,可以大概率測(cè)出其是否為素?cái)?shù)。利用了費(fèi)馬小定理和二次探測(cè)定理,證明這里不再放出.這里給大家提供一個(gè)模板,實(shí)際上證明也不復(fù)雜,有興趣可以自己嘗試。內(nèi)容過(guò)多不重要的的就不浪費(fèi)時(shí)間了。
bool millerRabbin(int n){if(n<3)return n==2;int a=n-1,b=0;while(a%2==0)a/=2,++b; // test_time 為測(cè)試次數(shù),建議設(shè)為不小于 8 的整數(shù)以保證正確率,但也不宜過(guò)大,否則會(huì)影響效率for(inti=1,j;i<=test_time;++i){int x=rand()%(n-2)+2,v=quickPow(x,a,n);if(v==1||v==n-1)continue;for(j=0;j<b;++j){v=(long long)v*v%n;if(v==n-1)break;}if(j>=b)return 0;}return 1; }埃氏篩
埃拉托斯特尼篩法,簡(jiǎn)稱埃氏篩,是一種由希臘數(shù)學(xué)家埃拉托斯特尼所提出的一種簡(jiǎn)單檢定素?cái)?shù)的算法。要得到自然數(shù)n以內(nèi)的全部素?cái)?shù),必須把不大于根號(hào)n的所有素?cái)?shù)的倍數(shù)剔除,剩下的就是素?cái)?shù)。它的實(shí)際運(yùn)用流程其實(shí)也很好理解,我們先吧2-n之間的整數(shù)寫(xiě)出來(lái),2是最小的素?cái)?shù),把表中2的所有倍數(shù)刪去,然后現(xiàn)在表中最小的素?cái)?shù)是3,由于他沒(méi)有被比他小的素?cái)?shù)整除,他一定是現(xiàn)在最小的素?cái)?shù),我么你繼續(xù)刪去3的所有倍數(shù),以此類推,直到篩到根號(hào)n為止。
算法時(shí)間復(fù)雜度是O(nlog(logn)).在數(shù)據(jù)范圍很大時(shí)基本可以看做線性復(fù)雜度,足以通過(guò)大部分題目。
for(int i=2;i<=N;i++){if(!vis[i]){for(int j=2*i;j<=N;j+=i){vis[j]=1;}}}這里是最基礎(chǔ)埃氏篩,一般其實(shí)夠用了,不過(guò)在某些情況下,我們還是需要對(duì)埃氏篩進(jìn)行優(yōu)化,這個(gè)優(yōu)化從三個(gè)部分體現(xiàn)出來(lái),我們分別來(lái)看他們是如何優(yōu)化的。
1.首先是第一重優(yōu)化,其實(shí)就是外層i的枚舉只需要到根號(hào)n即可,這樣足夠保證n以內(nèi)的數(shù)被篩過(guò)。很好理解
2.第二重優(yōu)化是一個(gè)小細(xì)節(jié)的優(yōu)化,在第二重循環(huán)中,每次增加素?cái)?shù)的1倍,但除了2以外素?cái)?shù)都是奇數(shù),那么奇數(shù)乘以偶數(shù)是偶數(shù),偶數(shù)情況早已被2篩完,故每次增加2i倍.
3.第三重優(yōu)化是在第二層循環(huán)中把j的起點(diǎn)從i*i開(kāi)始,因?yàn)楹Y到i的時(shí)候已經(jīng)能保證 i * i范圍內(nèi)沒(méi)有合數(shù)了。
通過(guò)這三種常數(shù)優(yōu)化,整個(gè)埃氏篩的復(fù)雜度幾乎和真正的線性篩相差無(wú)幾,甚至更快一點(diǎn)
for (int i = 0; i <= n; ++i) is_prime[i] = true; is_prime[0] = is_prime[1] = false; for(int i=2;i*i<=N;i++)//將i<=N換成i*i<=N {if(is_prime[i]){ int mul;//2篩過(guò)后偶數(shù)一定不是素?cái)?shù)所以直接加2; i==2?mul=1:mul=2;for(int j=i*i;j<=N;j=j+i*mul)//將2*i換成i*iis_prime[j]=false;} }歐拉篩
埃氏篩已經(jīng)很接近極限了,但是他還是可以繼續(xù)優(yōu)化,在埃氏篩的過(guò)程中有的合數(shù)會(huì)被多次標(biāo)記,浪費(fèi)時(shí)間,如何解決這個(gè)問(wèn)題呢?這就是是埃氏篩和歐拉篩的最大區(qū)別了
歐拉篩思想的核心是要保證的是每個(gè)合數(shù)只被這個(gè)合數(shù)最小的質(zhì)因子篩除,而且只篩一次,沒(méi)有重復(fù)篩除,這里就需要用到這行神秘代碼if(i%prime[j]==0) break;。歐拉篩的難點(diǎn)就在于對(duì)if (i % prime[j] == 0)這步的理解,當(dāng)i是prime[j]的整數(shù)倍時(shí),記 m = i / prime[j],那么 i * prime[j+1] 就可以變?yōu)?(m * prime[j+1]) * prime[j],這說(shuō)明 i * prime[j+1] 是 prime[j] 的整數(shù)倍,不需要現(xiàn)在篩出,因?yàn)樵谥蠛Y除過(guò)程中i * prime[j+1] 這個(gè)合數(shù)一定會(huì)被prime[j]篩除,prime[j]之后的所有素?cái)?shù)同理,所以break跳出循環(huán)。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6+10; int cnt=0; //記錄已知素?cái)?shù)數(shù)量 vector<int> primes; //存放素?cái)?shù)的不定長(zhǎng)數(shù)組 bool judge[maxn]; //篩除標(biāo)記 int main() {int i,j;memset(judge,true,sizeof(judge));judge[0]=false;judge[1]=false;// 0和1都不是素?cái)?shù)for(i=2;i<maxn;i++){if(judge[i]){primes.push_back(i);cnt++;//記錄素?cái)?shù)個(gè)數(shù)}for(j=0;j<cnt && i*primes[j]<=maxn;j++){judge[i*primes[j]]=false; //篩除if(i%primes[j]==0) //關(guān)鍵代碼break;}}vector<int>::iterator it;for(it=primes.begin();it!=primes.end();it++)//遍歷printf("%d\n",*it);return 0; }他能做到真正的線性復(fù)雜度,因?yàn)樗鼙WC每個(gè)數(shù)只被篩了一次。
歐拉篩篩歐拉函數(shù)
如果只是篩質(zhì)數(shù),大部分情況埃氏篩就夠了,那么歐拉篩還有什么用呢?他當(dāng)然不知可以用來(lái)篩素?cái)?shù),記得在介紹歐拉定理時(shí)候就留下了一個(gè)問(wèn)題,如何求歐拉函數(shù)。這里我們提供給大家兩個(gè)方法,大家可以根據(jù)題目情況自行選擇合適的方法。
1.公式法求歐拉函數(shù)
φ(a)=n?(1?1p1)?(1?1p2)?……?(1?1pn)φ(a)=n?(1-\frac{1}{p_1})?(1-\frac{1}{p_2})?……?(1-\frac{1}{p_n}) φ(a)=n?(1?p1?1?)?(1?p2?1?)?……?(1?pn?1?)
其中p1到pn是a用唯一分解定理分解出的所有質(zhì)因數(shù)。
這里簡(jiǎn)單提一下唯一分解定理,他也叫算數(shù)基本定理,任何一個(gè)大于1的自然數(shù) N,如果N不為質(zhì)數(shù),那么N可以唯一分解成有限個(gè)質(zhì)數(shù)的乘積
。最早的證明也是歐幾里得給出的,涉及到歐幾里得引理等內(nèi)容,跟acm關(guān)系不大,主要是數(shù)論方向的一個(gè)重要定理,證明這里就不給出了,有興趣可以自己查查資料,或者私下來(lái)找我交流。
那么這個(gè)公式是如何得到的呢?這里需要用到一些積性函數(shù)的知識(shí),如果要一起講的話時(shí)間肯定也是不夠的,所以暫時(shí)也不證了,后續(xù)講積性函數(shù)相關(guān)內(nèi)容后很多東西都能聯(lián)系起來(lái)了。這里需要注意的是,在網(wǎng)上有很多資料用這個(gè)公式證明了歐拉函數(shù)是積性函數(shù),需要注意的是,這種證法其實(shí)完全沒(méi)有意義。因?yàn)檫@個(gè)公式是用歐拉函數(shù)是積性函數(shù)推出來(lái)的,要搞清楚因果關(guān)系.
inline LL eular(LL x) {LL sum=x,y=x;for (LL i=2;i*i<=y;++i){if (x%i!=0) continue;sum=sum/i*(i-1); while (x%i==0) x/=i;}if (x>=2) sum=sum/x*(x-1);return sum; }2.歐拉篩求歐拉函數(shù)
這個(gè)過(guò)程會(huì)涉及積性函數(shù)的一些性質(zhì)。如果要證明推式子,前置知識(shí)太多了,所以不給大家證了,直接作為結(jié)論給出,相關(guān)證明和知識(shí)網(wǎng)上也有很多,感興趣的同學(xué)可以自己看一看。這三個(gè)性質(zhì)中的p就是歐拉篩中用來(lái)篩當(dāng)前這個(gè)數(shù)的最小質(zhì)因子。
如果i是素?cái)?shù),那么φ(i)=i?1如果imodp=0,那么φ(i?p)=p?φ(i)如果imodp≠0,i和p互質(zhì)那么φ(i?p)=φ(i)?φ(p)=φ(i)?(p?1)如果i是素?cái)?shù),那么φ(i)=i?1\\ 如果i mod p=0,那么φ(i?p)=p?φ(i)\\ 如果i mod p≠0,i和p互質(zhì)那么φ(i?p)=φ(i)?φ(p)=φ(i)*(p?1) 如果i是素數(shù),那么φ(i)=i?1如果imodp=0,那么φ(i?p)=p?φ(i)如果imodp?=0,i和p互質(zhì)那么φ(i?p)=φ(i)?φ(p)=φ(i)?(p?1)
inline void findphi(void) {phi[1]=1,prm[1]=0;for (LL i=2;i<=n;++i){if (!v[i]) {prm[++cnt]=i;phi[i]=i-1;//歐拉函數(shù),質(zhì)數(shù)的歐拉函數(shù)值為本身-1;}for (LL j=1;j<=cnt && i*prm[j]<=n;++j){v[i*prm[j]]=1;if (i%prm[j]==0) {phi[i*prm[j]]=phi[i]*prm[j]; break; }if (i%prm[j]!=0) phi[i*prm[j]]=phi[i]*(prm[j]-1); } }return; }八.快速冪
可以再O(logn)復(fù)雜度內(nèi)解決問(wèn)題,其實(shí)本質(zhì)就是把要求的數(shù)的冪次看成二進(jìn)制的數(shù),然后然后分解成二進(jìn)制的每一位。在進(jìn)行相乘。
a11=a23?a21?a20a^{11}=a^{2^3}*a^{2^1}*a^{2^0} a11=a23?a21?a20
分解之后我們只需要知道a1,a2,a4,…。用logn次乘法就能算出an。
如果需要取模,就在每一步直接加上取模操作即可
long long pow(long long a,long long b,long long m) {a%=m;long long res=1;while(b>0) {if(b&1)res=res*a%m;a=a*a%m;b>>=1;}return res; }九.逆元的應(yīng)用和幾種求法
1.逆元的定義
當(dāng) ax≡1(modb), x即為 a 在mod b 意義下的逆元。
逆元的數(shù)學(xué)符號(hào)是 inv ,a 在mod b 意義下的逆元記作 inv(a,b)。注意不要寫(xiě)反了。
簡(jiǎn)單來(lái)說(shuō)逆元就是在mod某個(gè)數(shù)意義下的倒數(shù)例如5x≡1(mod3)x=2是滿足10=1(mod3)所以稱2是5在mod3意義下的逆元、這里需要特別注意只有a和p互質(zhì),a才有關(guān)于p的逆元。
2.逆元的應(yīng)用
那么逆元有什么用呢?
(a + b) % p = (a%p + b%p) %p (對(duì))
(a - b) % p = (a%p - b%p) %p (對(duì))
(a * b) % p = (a%p * b%p) %p (對(duì))
(a / b) % p = (a%p / b%p) %p (錯(cuò))
在求余的過(guò)程中我們發(fā)現(xiàn)只有除法是不能分開(kāi)運(yùn)算的,而當(dāng)a過(guò)大時(shí),在計(jì)算除法過(guò)程中可能會(huì)造成比較大的精度損失,所以對(duì)于這種情況我們一般會(huì)把式子轉(zhuǎn)換成那么(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p來(lái)進(jìn)行計(jì)算。這樣就解決了除法不能分開(kāi)計(jì)算的問(wèn)題。
知道了這些那么我們繼續(xù)探討逆元是如何得到的。這里給大家提供4種方法,在不同的情況選選擇合適的方法來(lái)解決逆元。
3.逆元的4種求法
1.費(fèi)馬小定理求逆元
上面我們也介紹了費(fèi)馬小定理。
若p為質(zhì)數(shù),且gcd(a,p)=1,那么就有:
ap?1≡1(modp)a^{p-1} \equiv 1 \quad (mod \quad p) ap?1≡1(modp)
如果我們的模數(shù)p是質(zhì)數(shù)。我們就可以用費(fèi)馬小定理來(lái)求一個(gè)數(shù)的逆元。
因?yàn)橘M(fèi)馬小定理規(guī)定了p一定為一個(gè)質(zhì)數(shù),所以a和p一定互質(zhì)
那么雙方在modp的意義下同時(shí)除a可得
a^(p-2) ≡1/a (mod p)
也就是a^(p-2) ≡ inv(a) (mod p)
所以inv(a) = a^(p-2) (mod p)
代碼需要用快速冪實(shí)現(xiàn),復(fù)雜度大概為o(logn)
適用情況
如果模數(shù)是質(zhì)數(shù)的情況,這種方法是最快也是最好寫(xiě)的。一般mod數(shù)是質(zhì)數(shù)我們都會(huì)用這個(gè)。相信很多人應(yīng)該也猜到如果模數(shù)是合數(shù)我們?cè)撊绾瓮茝V這個(gè)式子,其實(shí)就是把費(fèi)馬小定理推廣到歐拉定理。不過(guò)這種方法需要O(n)線性篩先篩歐拉函數(shù),在快速冪算逆元。復(fù)雜度不夠優(yōu)秀而且寫(xiě)起來(lái)很復(fù)雜。所以沒(méi)有必要。只是幫大家理解。并且需要注意的是,歐拉定理的前提條件也是a和p互素。剛好和有逆元的條件一樣。
aφ(p)≡1(modp)aφ(p)?1就是a的逆元了a^{\varphi(p)}\equiv 1(modp)\\ a^{\varphi(p)-1}就是a的逆元了 aφ(p)≡1(modp)aφ(p)?1就是a的逆元了
2.擴(kuò)展歐幾里得算法求逆元
如果gcd(a,p)=1;
那么就有ax+py=1
雙方同時(shí)modp
就有ax≡1(modp)
因?yàn)閜y是p的倍數(shù)全部約掉了
此時(shí)x就是a的逆元
所以只需解出該情況下的擴(kuò)展歐幾里得方程的解問(wèn)題就解決了這里和上面介紹的擴(kuò)展歐幾里得解方程方法一樣。
適用情況
只要存在逆元,就可以用exgcd來(lái)求解逆元.復(fù)雜度為O(logn),也是我們最常用的一種方法。適用于要求的逆元個(gè)數(shù)不多的情況。
3.遞推求逆元
我們來(lái)簡(jiǎn)單推一下這個(gè)逆元的式子。
P是模數(shù),i是我們要求逆元的數(shù),那么實(shí)際上我們的實(shí)際目標(biāo)就是求i?1在modp意義下的值首先p=k?i+r.其中k=p/i,r=pmodi也就是k?i+r≡0(modp)稍微轉(zhuǎn)化一下,同乘r?1?i?1可以得到式子k?r?1+i?1≡0(modp)那么就有i?1≡?k?r?1(modp)再把最開(kāi)始的k和r的值帶進(jìn)來(lái)。公式最后就變成了i?1≡?p/i?inv[pmodi];它的遞推起點(diǎn)就是1的逆元在任意情況下均為1;、、最后式子可以看成if(i=1)inv(i)≡1(modp)elseinv(i)≡?p/i?inv[pmodi](modp)P是模數(shù),i是我們要求逆元的數(shù),那么實(shí)際上我們的實(shí)際目標(biāo)就是求i^{-1}在modp意義下的值\\ 首先p=k*i+r.其中k=p/i,r=p \quad mod\quad i\\ 也就是k*i+r\equiv0(modp)\\ 稍微轉(zhuǎn)化一下,同乘r^{-1}*i^{-1}\\ 可以得到式子k*r^{-1}+i^{-1}\equiv0(modp)\\ 那么就有i^{-1}\equiv-k*r^{-1}(modp)\\ 再把最開(kāi)始的k和r的值帶進(jìn)來(lái)。公式最后就變成了\\ i^{-1}\equiv-p/i*inv[pmodi];\\ 它的遞推起點(diǎn)就是1的逆元在任意情況下均為1;、、 最后式子可以看成\\ if(i=1)inv(i)\equiv1(modp)\\ else\quad inv(i)\equiv-p/i*inv[pmodi](modp) P是模數(shù),i是我們要求逆元的數(shù),那么實(shí)際上我們的實(shí)際目標(biāo)就是求i?1在modp意義下的值首先p=k?i+r.其中k=p/i,r=pmodi也就是k?i+r≡0(modp)稍微轉(zhuǎn)化一下,同乘r?1?i?1可以得到式子k?r?1+i?1≡0(modp)那么就有i?1≡?k?r?1(modp)再把最開(kāi)始的k和r的值帶進(jìn)來(lái)。公式最后就變成了i?1≡?p/i?inv[pmodi];它的遞推起點(diǎn)就是1的逆元在任意情況下均為1;、、最后式子可以看成if(i=1)inv(i)≡1(modp)elseinv(i)≡?p/i?inv[pmodi](modp)
有了這個(gè)式子。我們就可以直接用遞推的方式O(n)求出1-n所有數(shù)的逆元。
LL inv[mod+5]; void getInv(LL mod) {inv[1]=1;for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod; }適用情況
時(shí)間復(fù)雜度O(n)一般用于模數(shù)是素?cái)?shù)且要多次調(diào)用逆元的情況,比如盧卡斯定理等。
遞歸求逆元
到這里其實(shí)也很容易發(fā)現(xiàn)了,如果我們只是單獨(dú)求某個(gè)數(shù)的逆元。完全可以用遞歸的方法去做,用的還是上面給出的公式.
LL inv(LL i) {if(i==1)return 1;return (mod-mod/i)*inv(mod%i)%mod; }適用情況
復(fù)雜度O(n1/3)至于它的復(fù)雜度證明比較復(fù)雜,但是實(shí)際情況復(fù)雜度可以看做o(logp)p是模數(shù)。為什么有兩種復(fù)雜度呢?這個(gè)算法本質(zhì)上有兩種上界。并且大小不定。實(shí)際運(yùn)算復(fù)雜度應(yīng)該會(huì)取兩種復(fù)雜度中低的一種。根據(jù)實(shí)驗(yàn)。大部分情況,這個(gè)算法的復(fù)雜度都是logp級(jí)別的,也就是說(shuō)它的復(fù)雜度不會(huì)超過(guò)O(n1/3).那么他可以保證一定比擴(kuò)展歐幾里得和費(fèi)馬小定理優(yōu)秀,至于上界為什么是O(n^1/3).有一篇論文給出了比較詳細(xì)的證明
https://cs.uwaterloo.ca/research/tr/1996/21/cs-96-21.pdfcs-96-21.dvi (uwaterloo.ca)
如果模數(shù)是質(zhì)數(shù)直接用就行。代碼也短。
十.中國(guó)剩余定理(CRT)
孫子定理是中國(guó)古代求解一次同余式組的方法。是數(shù)論中一個(gè)重要定理。又稱中國(guó)余數(shù)定理。一元線性同余方程組問(wèn)題最早可見(jiàn)于中國(guó)南北朝時(shí)期(公元5世紀(jì))的數(shù)學(xué)著作《孫子算經(jīng)》卷下第二十六題,叫做“物不知數(shù)”問(wèn)題,原文如下:
有物不知其數(shù),三三數(shù)之剩二,五五數(shù)之剩三,七七數(shù)之剩二。問(wèn)物幾何?即,一個(gè)整數(shù)除以三余二,除以五余三,除以七余二,求這個(gè)整數(shù)。《孫子算經(jīng)》中首次提到了同余方程組問(wèn)題,以及以上具體問(wèn)題的解法,因此在中文數(shù)學(xué)文獻(xiàn)中也會(huì)將中國(guó)剩余定理稱為孫子定理。
解決這個(gè)問(wèn)題就要用到中國(guó)剩余定理。我們來(lái)看一下他的內(nèi)容是什么
這里我們得到是一組構(gòu)造出來(lái)的特殊接,方程的通解就是x+km如果要求最小非負(fù)解。只需要把x對(duì)m取模。保證x在0-m-1的范圍內(nèi)即為最小正整數(shù)解。
這里的ti其實(shí)就是Mi的逆元
那么這個(gè)問(wèn)題寫(xiě)在代碼中就只需要求逆元即可。
const ll N = 5e5 + 7; int n, a[N], m[N]; ll exgcd(ll a, ll b, ll &x, ll &y) {if(b == 0){x = 1; y = 0;return a;}ll d = exgcd(b, a % b, x, y);ll z = x;x = y;y = z - (a / b) * x;return d; } int main() {ll M = 1;scanf("%d", &n);for(int i = 1; i <= n; ++ i) {scanf("%d%d", &m[i], &a[i]);M *= m[i];}ll res = 0;for(int i = 1; i <= n; ++ i) {ll Mi = M / m[i];ll ti, y;//exgcd求逆元:解同余方程:ax + my = 1;(ax ≡ 1 mod m)ll d = exgcd(Mi, m[i], ti, y);ti = (ti % m[i] + m[i]) % m[i];res += a[i] * ti * Mi;}printf("%lld\n", (res % M + M) % M);//可能為負(fù)數(shù),所以需要處理一下return 0; }小結(jié)
今天講的內(nèi)容其實(shí)只是數(shù)論知識(shí)的冰山一角,并且有些地方的證明還需要一些其他知識(shí)來(lái)輔助。希望大家在學(xué)習(xí)數(shù)論的過(guò)程中還是要注重理解證明。這些證明思想會(huì)是你學(xué)習(xí)過(guò)程中最大的收獲。本文盡可能用簡(jiǎn)單的語(yǔ)言和基礎(chǔ)知識(shí)給一些數(shù)論基礎(chǔ)知識(shí)做了鋪墊。希望能幫到大家.有的地方latax掛掉了。換成圖片了
總結(jié)
- 上一篇: 下载开发证书步骤(自用备忘)
- 下一篇: JVM-垃圾收集器