歐拉定理和擴展歐拉定理可以解決形如5100000000000000000000等大數冪取模或者求ax mod n=1的大於1的最小x值等一類問題,其中歐拉函數占巨大的重要性,有效的將復雜的大數冪取模問題轉化為簡單的大數取模和快速冪問題,下面就來介紹一下基本的歐拉定理和擴展歐拉定理
歐拉函數的定義
歐拉函數Φ(n)指1至n以內,與n互質的數的個數
當n比較小時,可以通過從前到后遍歷的方法計算Φ(n)
對於歐拉函數Φ(n),還有更簡單的計算方法。
假如n可以通過分解質因子得到n的質因子$p_1,p_2,p_3,...,p_n$;
那么
$\phi (n) = n(1-\frac{1}{p_1})(1-\frac{1}{p_2})(1-\frac{1}{p_3})...(1-\frac{1}{p_n})$
特別的,當$n=p^r$(p為質數)時(p是n的唯一質因子)
$\phi (n) = p^r-count(p,2p,3p,...,p^r)$ (證明:由於n分解質因子只有p這一個質因子,故n以內與n不互質的數為p的倍數)
當n=p1r1p2r2時(p1和p2是n的所有質因子)
含有p1的因子為p1,2p1.....p2r2p1r1-1,p2r2p1r1,一共n/p1個;
含有p2的因子為p2,2p2.....p1r1p2r2-1,p1r1p2r2,一共n/p2個;
既含有p1又含有p2的因子為p1p2,2p1p2,3p1p2.....p1r1-1p2r2-1,一共n/p1p2個;
根據容斥定理可知與n不互質數的個數k為n/p1+n/p2-/p1p2;
那么Φ(n)=n-k=n*(1-1/p1)*(1-1/p2);
因此可以推廣到n=p1r1p2r2....pnrn
1 ll getDivisor(){ 2 ll sum=p; 3 int i; 4 for(i=2;i*i<=p;i++){ 5 if(p%i==0) sum=(long long)sum*(1-1.0/i); 6 while(p%i==0) p=p/i; 7 } 8 if(p!=1) sum=(long long)sum*(1-1.0/p); 9 return sum;//sum為歐拉函數的值 10 }
歐拉函數的性質
1)當n為質數時 Φ(n)=n-1
2)當m與n互質時 Φ(mn)=Φ(n)*Φ(m)
3)當n為質數時 Φ(2n)=Φ(n)
例如
Φ(6)=Φ(3)=2 或Φ(6)=Φ(3)*Φ(2)=2*1=2
歐拉定理
若n與a互質,則aΦ(n)≡1(mod n)
首先來解釋一下 aΦ(n)≡1(mod n) 是什么意思:
aΦ(n)≡1(mod n) 等價於 aΦ(n) mod n==1 mod n
應用:可以解決ax=1(mod n)或ax mod n=1 mod n的最小x的值的問題
由於最小x一定小於等於Φ(n),則令Φ(n)=t*x+b,但由於我們算的x是最小的x,故b等於0,可以得到Φ(n)是x的倍數,因此只要遍歷Φ(n)的所有大於logan因數,直到找到一個因數k使得ak≡1(mod n)成立,則k就是最小的x。
比如讓你求4x mod 3=1的最小x值:
4與3本身互質,最小x一定小於等於Φ(3)=2,由於2的大於log43的最小因數為1使得4x mod 3=1成立,故x最小取1
特別的,當n是質數時,ax=1(mod n)的最小x值為n-1
擴展歐拉定理
一個公式:ax ≡ ax mod Φ(n)+Φ(n) mod n (n與x與a無需滿足任何條件)
應用:運用擴展歐拉定理可以解決大數冪求模的問題,將利用大數求余很大的x轉化為約等於Φ(n)的一個數,然后可以利用快速冪的方法得到結果!
如讓你求49876543210213657854521 mod 12345的值:
可以通過分解質因子方法得到Φ(12345)=6576,再利用大數求余求9876543210213657854521 mod 6576=569,再用快速冪求4569+6576 mod 12345=9319
1 #include <iostream> 2 #include <algorithm> 3 #include <string> 4 #include <sstream> 5 #define ll1 (ll)1 6 using namespace std; 7 typedef long long ll; 8 stringstream stream; 9 ll p,divisor,pow1; 10 string str; 11 int t; 12 ll qSort(){ 13 ll x=4,sum=1; 14 while(pow1!=0){ 15 if(pow1%2!=0) sum=sum*x%p; 16 pow1=pow1>>1; 17 x=x*x%p; 18 } 19 return sum; 20 } 21 ll getPow(){ 22 stream<<str; 23 ll sum=0; 24 char c; 25 while(stream>>c) 26 sum=(10*sum+c-'0')%divisor; 27 stream.clear(); 28 return sum+divisor; 29 } 30 ll getDivisor(){ 31 ll sum=p; 32 int i; 33 ll p1=p; 34 for(i=2;i*i<=p1;i++){ 35 if(p1%i==0) sum=ll1*sum*(1-1.0/i); 36 while(p1%i==0) p1=p1/i; 37 } 38 if(p1!=1) sum=ll1*sum*(1-1.0/p1); 39 return sum; 40 } 41 int main(){ 42 cin>>t; 43 while(t--){ 44 cin>>str>>p; 45 divisor=getDivisor(); 46 pow1=getPow(); 47 cout<<qSort()%p<<endl; 48 } 49 return 0; 50 }
總結代碼
擴展歐拉定理求an mod p代碼:
#include <iostream> #include <string> #include <sstream> using namespace std; long long getOula_function(long long _mod){ long long i,sum=_mod; for(i=2;i*i<=_mod;i++){ if(!(_mod%i)) sum=(long long)sum*(1.0-1.0/i); while(!(_mod%i)) _mod/=i; } if(_mod!=1) sum=(long long)sum*(1.0-1.0/_mod); return sum; } long long getDivisor_function(string _str,long long _oula){ char c; long long sum=0; stringstream _stream; _stream<<_str; while(_stream>>c) sum=(10*sum+c-'0')%_oula; _stream.clear(); return sum; } long long qPow_function(long long _x,long long _divisor,long long _mod){ long long sum=1; while(_divisor){ if(_divisor&1) sum=sum*_x%_mod; _divisor=_divisor>>1; _x=_x*_x%_mod; } return sum; } long long pow_bigmod(long long _x,string _str,long long _mod){ long long _oula=getOula_function(_mod); long long _divisor=getDivisor_function(_str,_oula)+_oula; return qPow_function(_x,_divisor,_mod)%_mod; }
使用說明:首先放到代碼里面,在main函數中輸入pow_bigmod(long long a,string n,long long p),即可返回an mod p的值
歐拉定理求ax≡1(mod n)最小x代碼:
#include <iostream> #include <cmath> using namespace std; long long getOula_function(long long _mod){ long long i,sum=_mod; for(i=2;i*i<=_mod;i++){ if(!(_mod%i)) sum=(long long)sum*(1.0-1.0/i); while(!(_mod%i)) _mod/=i; } if(_mod!=1) sum=(long long)sum*(1.0-1.0/_mod); return sum; } long long qPow_function(long long _x,long long _divisor,long long _mod){ long long sum=1; while(_divisor){ if(_divisor&1) sum=sum*_x%_mod; _divisor=_divisor>>1; _x=_x*_x%_mod; } return sum; } long long getMinDiv(long long _base,long long _mod){ long long _divisor=getOula_function(_mod); long long _flag=(long long)(log(_mod)/log(_base)); long long _top=(long long)sqrt(_divisor); for(long long i=_flag+1;i<=_top;i++) if(!(_divisor%i) && qPow_function(_base,i,_mod)==1) return i; for(long long i=_top;i>=1;i--) if(!(_divisor%i) && qPow_function(_base,_divisor/i,_mod)==1) return _divisor/i; }
使用說明:首先放到代碼里面,在main函數中輸入getMinDiv(long long base,long long mod),即可返回ax≡1(mod n)最小x的值。
ps:上面兩個代碼都包含了getOula_function(long long _mod)和qPow_function(long long _x,long long _divisor,long long _mod)函數!
例題
1.牛客練習賽44----D-小y的盒子:https://blog.csdn.net/weixin_43702895/article/details/89672825
