三、乘法逆元
一、定義
若在mod p意義下,對於一個整數a,有a*b≡1(mod p),那么這個整數b即為a的 乘法逆元,同時a也為b的乘法逆元
一個數有逆元的充分必要條件是gcd(a,p)=1,此時a才有對p的乘法逆元
二、逆元是干什么的呢
首先對於除法取模不成立,即(a / b) % p ≠ (a % p / b % p) % p 。
顯然數學家們是不能忍受這種局面的,他們扔出了“逆元”來解決這個問題。
因為取模運算對於乘法來說是成立的,逆元就是把除法取模運算轉化為乘法取模運算
(a/b)%p=m -------- 1
假設存在一個數x滿足 a*x%p=m ------- 2
由模運算對乘法成立,對1式兩邊同時乘以 b ,得到:
a%p=(m(b%p))%p
如果 a 和 b 均小於模數 p 的話,上式可以改寫為:
a =bm%p
等式兩邊再同時乘以 x, 聯立2式比較得到:
ax%p=m%p=xbm%p
因此可以得到:
bx%p=1
哎,x是b的逆元呀(x 在模運算的乘法中等同於 1/b, 這就是逆元的意義)
由以上過程我們看到,求取 (a/b)%p 等同於 求取 a∗(b的逆元)%p。 因此,求模運算的除法問題就轉化為就一個數的逆元問題了。
另一個角度,不僅僅是為了除法取模。比如輸入任意互質的兩個數b,p,要使等式b%p=1恆成立,顯然,要使等式恆成立必須借助一個數x(把這個數叫做b的逆元),使得bx%p==1
eg :b=22,p=5
顯然22%5!=1
22*3%5==1,22的逆元x=3;
三、求取一個數的逆元,有兩種方法
(1).費馬小定理
因為在算法競賽中模數p總是質數,所以可以利用費馬小定理 :
bp−1%p=1
可以直接得到 b 的逆元是 bp−2, 使用 快速冪 求解即可
所以bp-2即為b在 mod p 意義下的逆元
ll pow(ll a, ll n, ll p) //快速冪 a^n % p { ll ans = 1; while(n) { if(n & 1) ans = ans * a % p; a = a * a % p; n >>= 1; } return ans; } ll niyuan(ll a, ll p) //費馬小定理求逆元 { return pow(a, p - 2, p); }
(2)擴展歐幾里德
對於利用拓展歐幾里德算法求逆元,很顯然,如果bx%p=1,那么 bx+py=1(p=0,解x),直接利用 exgcd(b, p, x, y),則 (x%p+p)%p 即為 b 的逆元((x%p+p)%p為x的最小正整數解)。
void exgcd(ll a, ll b, ll &x, ll &y) //拓展歐幾里得算法 { if(!b) x = 1, y = 0; else { exgcd(b, a % b, y, x); y -= x * (a / b); } } ll niyuan(ll a, ll b) //求a對b取模的逆元 { ll x, y; exgcd(a, b, x, y); return (x + b) % b; }
模板題:https://www.cnblogs.com/-citywall123/p/10693865.html
來源https://blog.csdn.net/arrowlll/article/details/52629448
輸入:
輸入僅一行. 兩個正整數a和n
輸出:
輸出僅一行. 一個正整數
樣例解釋:
((22)2)2mod 998244353 = 256
數據范圍
a,n <= 1018
思路:
先求出指數,即 an-1(快速冪求解),並將指數對mod-1(因為mod是質數,那么φ(mod)= mod-1),再用更新后的指數做為新的指數用快速冪求解即可。
代碼如下:
#include<cstdio> typedef long long ll; ll a,n; const int M=998244353; ll mi(ll a,ll b,int mod) { ll re=1; a%=mod; while (b) { if (b&1) re=(re*a)%mod; a=(a*a)%mod; b>>=1; } return re; } int main() { scanf ("%lld%lld",&a,&n); ll t=mi(a,n-1,M-1); printf("%lld",mi(a,t,M)); return 0; }
課后例題://poj 3696
L,L <= 2*109. 問多少個8連在一起的數是L的倍數。如果不存在就輸出0.
//這里省略輸入輸出規則,請讀者自行注意
思路:
x個8連在一起可以寫成8*(10x-1)*9,假設d=gcd(L,8)。那么題目可以表達為:L | 8*(10x-1)*9 , 接下來我們做一些簡單的式子變形:
L | 8*(10x-1)/9 ←→ L*9 | 8*(10x-1) ←→ 9L/d | (10x-1) ←→ 10x ≡ 1 (mod 9L/d)
引理:對於任意互質的正整數a,n,滿足:ax≡1(mod n)最小的整數值 X0 是φ(n)的約數。
證明如下:
(反證法)假設X0不是φ(n)的約數,則φ(n)可以表示為:qX0 + r(0 <= r < X0)。題設有:aX0≡1(mod n),那么,aqX0≡1(mod n)且正整數a,n互質,所以有歐拉定理:
aφ(n)≡1(mod n),即aqX0 * ar≡1(mod n),繼而得出:ar≡1(mod n),此時r<X0,這與X0是最小的整數值矛盾,所以假設不成立。得證。
φ(9L/d)並將其約數帶入10x ≡ 1 (mod 9L/d)驗證是否成立即可。求歐拉函數和快速冪,時間復雜度為:O(√(n) * log2 n)。代碼如下:
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll n,mod; int Case; ll gcd(ll a,ll b) { return a%b==0 ? b : gcd(b,a%b); } ll Er(ll x) { ll re=x; for (ll i=2;i*i<=x;i++) { if (x%i==0) { re=re/i*(i-1); while (x%i==0) x/=i; } } if (x>1) re=re/x*(x-1); return re; } ll mul(ll a,ll b,ll p) { ll re=0; while (b) { if (b&1) re=(re+a)%p; a=2*a%p; b>>=1; } return re; } ll ksm(ll a,ll b,ll p) { ll re=1; a%=p; while (b) { if (b&1) re=mul(re,a,p); a=mul(a,a,p); b>>=1; } return re; } int main() { while (scanf ("%lld",&n)) { if (n==0) return 0; Case++; ll d=gcd(n,8); ll phi=Er(9*n/d); mod=9*n/d; ll flag=9223372036854775806; for (ll i=1;i*i<=phi;i++) { if (phi%i==0) { if (ksm(10,i,mod)==1) flag=min(flag,i); if (ksm(10,phi/i,mod)==1) flag=min(flag,phi/i); } } flag==9223372036854775806?printf("Case %d: 0\n",Case):printf("Case %d: %lld\n",Case,flag); } }
