1. 程式人生 > >數論之旅(一): 逆元逆元!

數論之旅(一): 逆元逆元!

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)); } }

推薦習題: hiho1530 分數取模。