數論篇4——逆元(數論倒數)


問題引入

對於取余運算,有一下一些性質:

 

但是唯獨除法是不滿足的:

 

 為什么除法錯的呢?很好證明:

 

 而對於一些題目,我們必須在中間過程中進行求余,否則數字太大,電腦存不下,那如果這個算式中出現除法,我們就需要逆元了,將除法運算轉換為乘法運算

逆元

定義:

 

對於c,可以說是特殊意義上的倒數,我們可以理解為要求在0,1,2……p-1之間找一個數,是的這個數和a相乘后再取模p,得到的結果為1。

現在就要在回到剛才的問題了,除以一個數等於乘上這個數的倒數,在除法取余的情況下,就是乘上這個數的逆元,即:

 

這樣就把除法,完全轉換為乘法了。

逆元的性質

唯一性

給定一個數a,若存在模p下的逆元c,c一定唯一。

自反性

c是a的逆元,a也是c的逆元。

逆元的求解

對於逆元的求解,如果n較小的話,是容易算出來的,例如,求3在模26下的逆元:

但是當n非常大的時候,手動求解就非常困難了。

擴展歐幾里得算法(extend_gcd)

$a\cdot a^{1}\equiv 1(mod\ b)$

模數可以不為質數,滿足gcd(a,b)=1即可

定義:

對於逆元的表達式可以做一些變換:

 

 當gcd(a,b)=1時,代入extend_gcd(a,b,x,y),得到的非負的x值,就是上面的$a^-1$

int extend_gcd(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    int q = extend_gcd(b, a % b, x, y);
    int temp = x;
    x = y;
    y = temp - a / b * y;
    return q;
}

費馬小定理

只適用於模數為質數的情況,如果模數不是質數,可以變換一下,用歐拉定理。

如果p是一個質數,且a不是p的倍數則有 

$a^{p-1}\equiv 1(mod\ p)$

根據同余除法定理,兩邊同除以a

$a^{p-2}\equiv a^{-1}(mod\ p)$

所以

 $a^{-1}= a^{p-2}(mod\ p)$

用快速冪求一下,復雜度O(logn)

線性遞推

只適用於模數為質數的情況

當p為質數時有$$a^{-1}=(p-[p/a])\cdot (p\%a)^{-1}\%p$$

證明:

 

1的逆元就是1,這個方法復雜度是$O(n)$,但並不是說比前兩個差,它可以在O(n)的復雜度內算出n個數的逆元。

inv[1] = 1;
for(int i = 2; i < p; ++ i)
    inv[i] = (p - p / i) * inv[p % i] % p;

階乘遞推求逆元

只適用於模數為質數的情況

設 $f(i)=inv(i\ !)$

則根據:$f(i-1)=\frac{1}{\ (i-1)\ !}=\frac{1}{i\ !}\times i =f(i)\times i$

有:$f(i-1) = f(i)\times i$

假設要求 $[1,n]$ 中所有數的逆元,先求得 $[1,n]$ 中所有數的階乘

可以用 費馬小定理 求得 $f(n)$ 的值,之后遞推出 $f(1 \sim n)$ 的值

但是 $inv(1! \sim n! )$ 並不是我們想要的答案,需要繼續轉化。

根據:$inv(i)=\frac{1}{i}=\frac{1}{i\ !}\times (i-1)\ ! = inv(i!)\times (i-1)!$

最終的轉換式 :$$inv(i) = inv(i!) \times(i-1)\ ! $$

時間復雜度也是$O(n)$

    int N,P;
    fact[0] = 1;
    //mod P 求階乘
    for (int i = 1; i <= N; i++) 
        fact[i] = fact[i - 1] * i % P;
    //求N! mod p的逆元
    inv[N] = quickPower(fact[N], P - 2, P) % P;
    //遞推求N!~1! mod p的逆元
    for (int i = N - 1; i >= 1; i--)
        inv[i] = inv[i + 1] * (i + 1) % P;
    //轉換輸出
    for (int i = 1; i <= N; i++) 
        printf("%d\n", (inv[i] * fact[i - 1]) % P); 

 逆元的應用

費這么大周章求出來的逆元究竟有什么用呢?

將除法轉換為乘法

已知$n$為任意整數,$(a,p)=1$,則$n\div a\ mod\ p = n\cdot a^{-1}\ mod\ p$,

比如 $12\div 4\ mod\ 7 = 12\cdot 2\ mod\ 7=3$。

具體使用情況就可以是上面提到的,一個取余運算式中間出現了除號。

但如果式子中沒有取余呢?那自己取一個唄,取一個特別大的素數(但是不能太大,推薦取1e9+7,好記,快速冪也不會爆long long范圍,可以運算所有int范圍的數據)

測試代碼:

const LL P = 1e9 + 7;

LL quickPower(LL a, LL n, LL p) {
    LL res = 1;
    while (n) {
        if (n & 1) {
            res = (res % p * a % p) % p;
        }
        a = (a % p * a % p) % p;
        n >>= 1;
    }
    return res;
}

int main() {
    LL _a = quickPower(8, P - 2, P);
    LL res = (72 * _a) % P;
    cout << res << endl;
    return 0;
}

注意以下幾點:

  • 如果真的要計算的數字特別大,可能需要考慮使用快速乘(龜速乘)。
  • 要注意實際是否會出現除不盡的情況,因為分數取模比較特殊,算出來的結果不是你想要的答案。
  • 具體使用看情況吧,俺還沒遇見過。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM