數論之旅(一): 逆元逆元!
阿新 • • 發佈:2019-01-22
1.基本概念:逆元可以理解為乘法中的倒數!
2.基礎知識準備: 求餘數。
前提條件: a,p互質。
在這裡我們需要了解 (a / b) % p = (a%p / b%p) %p 這樣是錯的。
舉個簡單的反例 (10 / 4)% 3 = 2, (10 % 3 / 4 % 3) % 3 = 0. 很明顯不一樣。
而對於加法,減法,乘法而言都是正確的。
所以,我們需要引入 逆元 的概念,來幫助我們解決問題—>對於一些題目,我們必須在中間過程中進行求餘,否則數字太大,會爆範圍。 我們假設逆元為 inv(b)。
(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p。
實現 除法—->乘法的轉變。
接下來我們來探究如何求解逆元。
方法一: 費馬小定理。a^(p-1) ≡1 (mod p) (p為質數)
->>a^(p-2) ≡inv(a) (mod p) ->> inv(a)=a^(p-2)
這裡可以直接用快速冪算出。複雜度 longn。
方法二:擴充套件歐幾里德演算法
歐幾里德有個十分有用的定理: gcd(a, b) = gcd(b , a%b) 。
所以我們可以在log的時間裡求出 a,b的gcd。
a*x + b*y = 1 (a,b互質)
對於兩邊%b
a*x % b + b*y % b = 1 % b
a*x % b = 1 % b
a*x = 1 (mod b)
-> x是a關於b的逆元
ll gcd(ll a,ll b){
return b == 0 ? a : gcd(b, a%b);
}
#include<cstdio>
typedef long long LL;
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}