求逆元、階乘逆元、線性求逆元



本文章內,若無特殊說明,數字指的是整數,除法指的是整除。

什么是逆元

我們稱\(a\)\(b\)在模\(p\)情況下的逆元,則有\(a \times b \equiv 1 ( mod\,\,p)\)
所以呢,我們其實可以將逆元看成一個數的相反數。所以在除以一個數的時候,就相當於乘上它的相反數。

如何求逆元

我們先來看看什么情況下有逆元。

當且僅當\(gcd(b,p)=1\)時,\(b\)在模\(p\)情況下有逆元。

這個結論可由裴蜀定理顯然推得,下面一段來自百度百科,若讀者對證明有興趣,可以自行了解。

裴蜀定理(或貝祖定理,Bézout's identity)得名於法國數學家艾蒂安·裴蜀,說明了對任何整數\(a\)\(b\)和它們的最大公約數\(d\),關於未知數\(x\)\(y\)的線性不定方程(稱為裴蜀等式):若\(a\),\(b\)是整數,且\((a,b)=d\),那么對於任意的整數\(x\),\(y\),\(ax+by\)都一定是\(d\)的倍數,特別地,一定存在整數\(x\),\(y\),使\(ax+by=d\)成立。

拓展歐幾里得求逆元

下面介紹如何用拓展歐幾里得求逆元。

我們求\(b\)在模\(g\)意義下的逆元,根據\(a \times b \equiv 1 ( mod\,\,p)\),得到\(a\times b + k\times p = 1\)
我們知道,\(gcd(b,p)=gcd(p,b \% p)\),所以\(a'\times p+k'\times (b \% p)=1\)同樣有解。而由於\(gcd(b,p)=1\),輾轉相除法時,總有\(a''\times 1 + k'' \times 0 = 1\)
此時我們不妨令\(a''=1,k''=0\)
現在我們考慮怎么推回去。

\[a'\times p+k'\times (b \% p)=1 \]

\[\Rightarrow a'\times p+k'\times( b-\left \lfloor \frac{b}{p} \right \rfloor \times p)=1 \]

\[\Rightarrow k'\times b+(a'-\left \lfloor \frac{b}{p} \right \rfloor \times k') \times p=1 \]

\(a\times b + k\times p = 1\)對照,得到\(a=k',\,\,\,k=a'- \left \lfloor \frac{b}{p} \right \rfloor\times k'\)。那么這樣,我們就得到了\(a\times b + k\times p = 1\)的一組解,同時,\(a\)就是\(b\)在模\(p\)下的逆元。
附C++程序

#include <bits/stdc++.h>
using namespace std;
void ExPower( int b, int p, int & a, int & k ) {
    if( p == 0 ) {
        a = 1; k = 0;
        return;
    }
    ExPower( p, b % p, k, a );
    k -= b / p * a;
    return;
}
int main() {
    int b, p;
    cin >> b >> p;
    int a, k;
    ExPower( b, p, a, k );
    if( a < 0 ) a += p;
    cout << a << endl;
    return 0;
}

費馬小定理求逆元

我們知道,當\(p\)為素數,並且\(gcd(a,p)=1\)時,我們有\(a^{p-1} \equiv 1 (mod\,\,p)\)。那么我們就有\(a^{p-2}\times a \equiv 1(mod \,\, p)\)。所以逆元就是\(a^{p-2}\)了。

階乘逆元

如果我們需要求\(0!\)\(n!\)的逆元,對於每個元素都求一遍,就顯得有點慢。(雖然\(exPower\)的時間快到可以認為是小常數。)
前面我們說了,逆元就可一看做是求倒數。那么不就有\(\frac{1}{(n+1)!}\times (n+1)=\frac{1}{n!}\)
附C++程序:

int inv( int b, int p ) {
    int a, k;
    exPower( b, p, a, k );
    if( a < 0 ) a += p;
    return a;
}
void init( int n ) {
    Fact[ 0 ] = 1;
    for( int i = 1; i <= n; ++i ) Fact[ i ] = Fact[ i - 1 ] * i % Mod;
    INV[ n ] = inv( Fact[ n ], Mod );
    for( int i = n - 1; i >= 0; --i ) INV[ i ] = INV[ i + 1 ] * ( i + 1 ) % Mod;
    return;
}

線性求逆元

按照上面的方法,如果我們要求\(1\)\(p-1\)關於\(p\)的逆元,而\(p\)較大時,時間復雜度有點吃不消。而我們有一種更強的做法,可以在\(O(p)\)的時間內解決。

對於當前的\(i\),我們設\(p=k\times i+r\)。於是:

\[\begin{aligned} k \times i + r & \equiv 0 &\,\,(mod \,\, p) \\ k \times i \times ( i^{-1} \times r ^{-1}) + r \times (i^{-1} \times r^{-1}) &\equiv 0 &\,\,( mod \,\, p) \\ k \times r^{-1} + i ^ {-1} & \equiv 0 &\,\, (mod \,\, p)\\ i^{-1} & \equiv -k \times r^{-1} &\,\, (mod \,\, p) \\ i^{-1} & \equiv - \left \lfloor \frac{p}{i}\right \rfloor \times r^{-1} &\,\,(mod\,\,p) \end{aligned} \]

所以代碼就大致如下:

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


免責聲明!

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



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