先介紹兩個數學定理。。。
同余
兩個整數a、b,若它們除以整數m所得的余數相等,則稱a與b對於模m同余或a同余於b模m。
記作:a≡b (mod m),
讀作:a同余於b模m,或讀作a與b對模m同余,例如26≡2(mod 12)。
定義
設m是大於1的正整數,a、b是整數,如果m|(a-b),則稱a與b關於模m同余,記作a≡b(mod m),讀作a與b對模m同余。
顯然,有如下事實
(1)若a≡0(mod m),則m|a;
(2)a≡b(mod m)等價於a與b分別用m去除,余數相同。
證明
充分性:m|(a-b)→a≡b(mod m)。
設a=mq1+r1,b=mq2+r2,
且0≤r1,r2<m,
∵m |(a-b),
又a-b=m(q1-q2)+(r1-r2)。
∴必有常數n使得(r1-r2)=mn。
則有m|(r1-r2)。
∵0≤r1,r2<m,
∴0≤|r1-r2|<m,
∴r1-r2=0,
即r1=r2,故a≡b(mod m)。
必要性:a≡b(mod m)→m|(a-b)。
設a,b用m去除余數為r,
即a=mq1+r,b=mq2+r。
∵m(q1-q2)=(a-b),
∴m|(a-b)。
費馬小定理
內容:如果p是一個質數,而整數a不是p的倍數,則有a^(p-1)≡1(mod p),所以有a^(p-2)=a-1
證明:
補充幾個定義:
剩余系:對於一個特定整數n,一個整數集中所有數模n的余數集
完全剩余系:設m∈Z+,若r0,r1,...rm-1為m個整數,並且兩兩模m不同余,則r0,r1,...rm-1叫作模m的一個完全剩余系。
引理1.
若a,b,c為任意3個整數,m為正整數,且m與c互質,則當a·c≡b·c(mod m)時,有a≡b(mod m)。
證明:a·c≡b·c(mod m)可得ac–bc≡0(mod m)可得(a-b)·c≡0(mod m)。因為(m,c)=1即m,c互質,c可以約去,a– b≡0(mod m)可得a≡b(mod m)。
證明:a·c≡b·c(mod m)可得ac–bc≡0(mod m)可得(a-b)·c≡0(mod m)。因為(m,c)=1即m,c互質,c可以約去,a– b≡0(mod m)可得a≡b(mod m)。
引理2.
設m是一個整數且m>1,b是一個整數且m與b互質。如果a[1],a[2],a[3],a[4],…a[m]是模m的一個完全剩余系,則b·a[1],b·a[2],b·a[3],b·a[4],…b·a[m]也構成模m的一個完全剩余系。
證明:若存在2個整數b·a[i]和b·a[j]同余即b·a[i]≡b·a[j](mod m)..(i>=1 && j>=1),根據引理1則有a[i]≡a[j](mod m)。根據完全剩余系的定義可知這是不可能的,因此不存在2個整數b·a[i]和b·a[j]同余。
設m是一個整數且m>1,b是一個整數且m與b互質。如果a[1],a[2],a[3],a[4],…a[m]是模m的一個完全剩余系,則b·a[1],b·a[2],b·a[3],b·a[4],…b·a[m]也構成模m的一個完全剩余系。
證明:若存在2個整數b·a[i]和b·a[j]同余即b·a[i]≡b·a[j](mod m)..(i>=1 && j>=1),根據引理1則有a[i]≡a[j](mod m)。根據完全剩余系的定義可知這是不可能的,因此不存在2個整數b·a[i]和b·a[j]同余。
所以b·a[1],b·a[2],b·a[3],b·a[4],…b·a[m]構成模m的一個完全剩余系。
構造素數
的完全剩余系


因為
,由引理2可得


也是p的一個完全剩余系。由完全剩余系的性質,

即

易知
,同余式兩邊可約去
,得到



這樣就證明了費馬小定理(摘自百度詞條,仔細想想確實很好懂)
擴展歐幾里得 (主要用於求解不定方程)
補充:歐幾里得算法(輾轉相除)
定義:對於已知a,b,必然存在ax+by=gcd(a,b)
證明:
由 ax+by=gcd(a,b) 得 bx1+(a%b)y1=gcd(b,a%b)
又∵a%b=a-[a/b]*b (此處[]為向下取整函數)
函數圖像為:
(會有用)
ax+by=bx1+(a-[a/b]*b)y1
化簡為:
a(x-y1)=b(x1-y-[a/b]*y1)
解得
x=y1
y=x1-[a/b]*y1
從頭遞歸求解就行。
怎樣求逆元
擴展歐幾里得
大概思路:
設a在模p意義下的逆元為x
則有 axΞ1(mod p)
∴ax=kp+1 (k為未知數)
∴ax-kp=1
利用擴展歐幾里得求不定方程解就好
#include<iostream> using namespace std; void exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return; } exgcd(b,a%b,y,x); y-=a/b*x; } int main() { int n,p; cin>>n>>p; int x,y; for(int i=1;i<=n;i++) { exgcd(i,p,x,y); x=(x%p+p)%p; cout<<x<<endl; } return 0; }
(長的跟想象中的不太一樣,着重記一記)
費馬小定理
大概思路:
∵a-1=a(p-2)
利用快速冪求解
#include<iostream> using namespace std; int fm(int a,int b,int p) { int base=a; int ans=1; while(b) { if(b&1) ans=(ans*base)%p; base=(base*base)%p; b=b/2; } return ans; } int main() { int n,p; cin>>n>>p; int p1=p-2; for(int i=1;i<=n;i++) { cout<<fm(i,p1,p)<<endl; } return 0; }
代碼大概長這樣
看一道題。。
可以每個數直接調用上述的兩種求逆元的方法,求解 時間復雜度O(nlogn)
還可以更優化一點,利用1,2,3,……n-1的逆元求n的逆元
(看不懂的話可以想一下模運算的加法原理)
(因為r小於i,所以r的逆元已知)
#include<iostream> #include<cstdio> using namespace std; long long int inv[3000003]; int main() { int n,p; cin>>n>>p; inv[1]=1; cout<<1<<endl; for(int i=2;i<=n;i++) { inv[i]=(long long int)(p-p/i)*inv[p%i]%p; printf("%d\n",inv[i]); } return 0; }
這里解釋一下為什么除以一個數的逆元等價於乘以它的逆元
例題:
(自己思考!!!!)
提示:列不定方程求解
(RP++!)