1. 程式人生 > >逆元模板 與簡介

逆元模板 與簡介

1.費馬小定理求逆元(求a對於mod的逆元,要求mod為素數)

由費馬小定理 a^(p-1)≡1 , 變形得 a*a^(p-2)≡1(mod p),答案已經很明顯了:若a,p互質,因為a*a^(p-2)≡1(mod p)且a*x≡1(mod p),則x=a^(p-2)(mod p),用快速冪可快速求之

  • 複雜度O(logn);
  • 適用範圍:一般在mod是個素數的時候用,比擴歐快一點而且好寫。
ll power_mod(ll a, ll b, ll mod)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
inv2 = power_mod(a, mod - 2, mod);
 

2.擴充套件歐幾里得

給定模數m,求a的逆相當於求解ax=1(mod m) 這個方程可以轉化為ax-my=1 然後套用求二元一次方程的方法,用擴充套件歐幾里得演算法求得一組x0,y0和gcd 檢查gcd是否為1 gcd不為1則說明逆元不存在 若為1(a與m互質),則調整x0到0~m-1的範圍中即可

  • PS:這種演算法效率較高,常數較小,時間複雜度為O(ln n)
  • 適用範圍:只要存在逆元即可求,適用於個數不多但是mod很大的時候,也是最常見的一種求逆元的方法。
typedef  long long ll;
void extgcd(ll a,ll b,ll& d,ll& x,ll& y)
{    
    if(!b)
        { d=a; x=1; y=0;}    
    else
    { 
        extgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}
ll inverse(ll a,ll n)     //求a在mod下的逆元,不存在逆元返回-1
{
    ll d,x,y;    
    extgcd(a,n,d,x,y);    
    return d==1?(x+n)%n:-1;  //(x+n)%n 防止為負
}

3.逆元打表

有時會遇到這樣一種問題,在模質數p下,求1~n逆元 n< p(這裡為奇質數)。可以O(n)求出所有逆元,有一個遞推式如下

                   

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

       

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

       

再把替換掉,最終得到

       

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

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

線性求逆元

typedef  long long ll;
const int N = 1e5 + 5;
int inv[N]; 
void inverse(int n, int p) 
{    
    inv[1] = 1;    
    for (int i=2; i<=n; ++i) 
    {        
        inv[i] = (ll) (p - p / i) * inv[p%i] % p;    
    }
}

注意:

  • 呼叫前要先預處理
  • 呼叫的時候要先對除數取mod

效能分析:

  • 時間複雜度O(n)
  • 適用範圍:p是不大的素數而且多次呼叫,比如盧卡斯定理。

遞迴求逆元

LL inv(LL i)
{
    if(i==1)return 1;
    return (mod-mod/i)*inv(mod%i)%mod;
}

效能分析

  • 時間複雜度:O(logmod)
  • 好像找到了最簡單的演算法了!!
  • 適用範圍: mod數是素數,所以並不好用,比如中國剩餘定理中就不好使,因為很多時候可能會忘記考慮mod數是不是素數。

參考部落格