1. 程式人生 > >乘法逆元的幾種計算方法

乘法逆元的幾種計算方法

乘法逆元是數論中重要的內容,也是 ACM 中常用到的數論演算法之一。所以,如何高效的求出乘法逆元是一個值得研究的問題。

這裡我們只討論當模數為素數的情況,因為如果模數不為素數,則不一定每個數都有逆元。

定義

在  mod p的意義下我們把 xx 的乘法逆元寫作 x ^ {-1}x1
乘法逆元有如下的性質:


乘法逆元的一大應用是模意義下的除法,除法在模意義下並不是封閉的,但我們可以根據上述公式,將其轉化為乘法。


費馬小定理

要求 pp 為素數。

上述公式可變形為

由乘法逆元的定義,a ^ {p - 2}ap2 即為 aa 的乘法逆元。

使用快速冪計算 a ^ {p - 2}ap2,總時間複雜度為 O(\log a)O(loga)

程式碼

inline int pow(const int n, const int k) {
    long long ans = 1;
    for (long long num = n, t = k; t; num = num * num % MOD, t >>= 1) if (t & 1) ans = ans * num % MOD;
    return ans;
}
inline int inv(const int num) {
    return pow(num, MOD - 2);
}

擴充套件歐幾里得

擴充套件歐幾里得(EXGCD)演算法可以在 O(\log \max(a, b))O(logmax(a,b)) 的時間內求出關於 xxyy 的方程


的一組整數解

當 bb 為素數時,\gcd(a, b) = 1gcd(a,b)=1,此時有

時間複雜度為 O(\log a)O(loga)

程式碼

void exgcd(const int a, const int b, int &g, int &x, int &y) {
    if (!b) g = a, x = 1, y = 0;
    else exgcd(b, a % b, g, y, x), y -= x * (a / b);
}
inline int inv(const int num) {
    int g, x, y;
    exgcd(num, MOD, g, x, y);
    return ((x % MOD) + MOD) % MOD;
}


遞推法


程式碼

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

下面是ACdreamers關於遞推求解逆元的推導過程(個人覺得他的更好)

其實有些題需要用到的所有逆元,這裡為奇質數。那麼如果用快速冪求時間複雜度為

如果對於一個1000000級別的素數,這樣做的時間複雜度是很高了。實際上有的演算法,有一個遞推式如下

                   

它的推導過程如下,設,那麼

       

對上式兩邊同時除,進一步得到

       

再把替換掉,最終得到

       

初始化,這樣就可以通過遞推法求出模奇素數的所有逆元了。

另外的所有逆元值對應中所有的數,比如,那麼對應的逆元是