1. 程式人生 > >淺談乘法逆元

淺談乘法逆元

~~ int amp 回顧 return 意義 元表 lin nlog

淺談乘法逆元

乘法逆元,一般用於求解\(\frac{A}{C}(mod ~ P)\)的值,因為我們通過模的定義可以知道上式顯然不等於\(\frac{A \% P}{B \% P}\)。例子有很多不再舉了。那麽如果我們要求上式大多數情況都需要借助逆元。

首先是定義:

\(A \times X \equiv 1 (mod ~ P)\),並且\(A \perp P\)(\(\perp\):互質),那麽我們就稱\(X\)\(A\)的逆元,簡稱為\(A^{- 1}\),也就是\(A\)\(mod ~ P\)意義下的倒數。

借此我們可以知道\(\frac{A}{C}(mod ~ P)\)的求法:我們先求出\(C\)

\(mod ~ P\)意義下的逆元,然後\(\times A\)就是答案了。那麽我們主要求的就是\(C\)的逆元。

擴展歐幾裏得版

首先從最簡單開始,就是一個擴展歐幾裏得,直接求解\(A \times X + P \times Y = 1\)就行了。

inline void Exgcd(int A, int P, int X, int Y) {
    if (! B) X = 1, Y = 0 ;
    else Exgcd(P, A % P, Y, X), Y -= A / B * X ;
}

這個方法雖然時間上比較慢,但是有一個優點,就是當\(A\)\(P\)不互質的時候,依然可以適用。但是下面介紹的其他方法就必須滿足\(A \perp P\)

了。

費馬小定理版

回顧一下費馬小定理的內容:

\(A \perp P\), 則\(A^{P - 1} \equiv 1(mod ~ P)\)

我們可以把它運用到乘法逆元裏。結合\(A \times X \equiv 1(mod ~ P)\)可以得到\(A \times X \equiv A^{P - 1}(mod ~ P)\)。當\(P\)為質數的時候,我們把左右都除以一個\(A\)可以得到\(X \equiv A^{P - 2}(mod P)\)。然後就可以一個快速冪求出來了。

inline void Fpm(int A, int P) {
    int Ans = 1 ; int M = P ;
    A %= M ; P -= 2 ;
    while (P) {
        if (P & 1) Ans = Ans * A % M ;
        A = A * A % M ; P >> = 1 ;
    }   return Ans % M ;
}

上面的復雜度是\(O(logN)\)的,在求多個逆元的時候可能\(O(NlogN)\)是過不去的。當然我們還有一種利用地推打出逆元表的方法做到\(O(N)\).

歐拉篩版

\(INV[i]\)\(i\)的逆元。我們知道有\(INV[A] = A ^ {P - 2}(mod ~ P),~INV[B] = B ^ {P - 2} (mod ~ P)\)

我們利用費馬小定理可以得\(INV[A \times B] = (AB) ^ {P - 2} (mod P)\)也就是說\(INV[A \times B] = INV[A] \times INV[B]\)。求得遞推式。我們就可以利用歐拉篩。

遞推版

為了方便起見就直接寫過程了。

\(P = kA + r~~(r \in [0, P)~)\)。因為我們知道任何一個數都可以寫成這個形式嘛。

那麽有\(kA + r \equiv 0 (mod ~ P)\)

因為\(A^{P - 1} \equiv 1(mod ~ P)\)

所以\((kA +r) \times A^{- 1} \times r^{- 1} \equiv 0 (mod ~ P)\)

把左邊的括號拆開得\(kr^{- 1} + A^{- 1} \equiv 0 (mod ~ P)\)

移項:\(A^{- 1} \equiv -kr^{- 1}\)

在這裏我們知道\(k = \lfloor \frac{P}{A} \rfloor\)

\(A^{- 1} \equiv -\lfloor \frac{P}{A} \rfloor \times r^{- 1}\)

並且我們還知道\(r ≤ A\)。那麽我們就可以借用數組\(INV[r]\)完成遞推。同時我們還知道\(r = P \% A\)

最後得到\(INV[A] = (P - \lfloor \frac{P}{A} \rfloor) \times INV[P\% A]\)

遞推完成,時間復雜度\(O(N)\)

for (int i = 2 ; i <= N ; i ++)
        INV[i] = P - (P / i) * INV[P % i] % P;

當然不要忘了初始化的問題。

淺談乘法逆元