【模板】乘法逆元

如果 ax1(modb) a x ≡ 1 ( mod b ) (a,b)=1 ( a , b ) = 1 ,则称 x x 是模 b b 意义下的 a a 的逆元,记为 a1 a − 1

逆元可以用来在计算 tamodb t a mod b 时,转化为 ta1modb t a − 1 mod b

Algorithm A l g o r i t h m

根据逆元的定义,可以转化为 ax+by=1 a x + b y = 1 ,用拓展欧几里得算法求解。

Code1 C o d e 1

\\求解单个数在模意义下的逆元(exgcd)
void Exgcd(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1; y = 0;
        return;
    }
    Exgcd(b, a % b, y, x);
    y -= a / b * x;
}
int Mod_Inverse(int a, int p) {
    int x, y;
    Exgcd(a, p, x, y);
    return (x + p) % p;
}

Code2 C o d e 2

int Exgcd(int a, int b, int& x, int& y) {
    if (a == 0 && b == 0) return -1;
    if (b == 0) {
        x = 1; y = 0;
        return a;
    }
    int value = Exgcd(b, a % b, y, x);
    y -= a / b * x;
    return value;
}
int Mod_Inverse(int a, int p) {
    int x, y, value = Exgcd(a, p, x, y);
    return value == 1 ? (x + p) % p : -1;
}

特殊地,当 b b 是个素数时,由费马小定理,可得 ab11(modb) a b − 1 ≡ 1 ( mod b ) ,则 a1ab2(modb) a − 1 ≡ a b − 2 ( mod b ) ,使用快速幂计算即可。


接下来讲线性算法求解给定 n n , p p 1 1 ~ n n 中所有整数在模 p p 意义下的乘法逆元。

Algorithm1 A l g o r i t h m 1

我们令 f(i)=i! f ( i ) = i ! , g(i)=(i!)1 g ( i ) = ( i ! ) − 1

很明显, f(i)=(i1)!i=f(i1)i f ( i ) = ( i − 1 ) ! ∗ i = f ( i − 1 ) ∗ i g(i)=((i+1)!)1(i+1)=g(i+1)(i+1) g ( i ) = ( ( i + 1 ) ! ) − 1 ∗ ( i + 1 ) = g ( i + 1 ) ∗ ( i + 1 )

这样,我们顺推出 f f 数组,再逆推出 g g 数组,那么 i1=i!((i1)!)1=f(i)g(i1) i − 1 = i ! ∗ ( ( i − 1 ) ! ) − 1 = f ( i ) ∗ g ( i − 1 )

所有等式均在模 p p 意义下进行。

Code1 C o d e 1

#include
#include
#include
#include
#define MAXN 3000010
long long f[MAXN], g[MAXN];
void Exgcd(int a, int b, int& x, int& y) {
    if (b == 0) {
        x = 1; y = 0;
        return;
    }
    Exgcd(b, a % b, y, x);
    y -= a / b * x;
}
int Mod_Reverse(int a, int p) {
    int x, y;
    Exgcd(a, p, x, y);
    return (x + p) % p;
}
int main() {
    int n, p;
    scanf("%d%d", &n, &p);
    f[0] = 1;
    for (int i = 1; i <= n; i++)
        f[i] = (f[i - 1] * i + p) % p;
    g[n] = Mod_Reverse(f[n], p); //p是质数的话可以直接快速幂
    for (int i = n - 1; i >= 1; i--) 
        g[i] = (g[i + 1] * (i + 1) + p) % p;
    for (int i = 1; i <= n; i++)
        printf("%lld\n", (f[i - 1] * g[i] + p) % p);
    return 0;
}

Algorithm2 A l g o r i t h m 2

首先

111(modp) 1 − 1 ≡ 1 ( mod p )

我们设 p=ki+r p = k ∗ i + r ,且 1<r<i<p 1 < r < i < p ,那么这个式子在模 p p 意义下就会得到 ki+r0(modm) k ∗ i + r ≡ 0 ( mod m ) ,两边同乘以 i1,r1 i − 1 , r − 1 可得:

kr1+i1i1i1i10kr1[pi](pmodi)1(p[pi])(pmodi)1(modp)(modp)(modp)(modp) k ∗ r − 1 + i − 1 ≡ 0 ( mod p ) i − 1 ≡ − k ∗ r − 1 ( mod p ) i − 1 ≡ − [ p i ] ∗ ( p mod i ) − 1 ( mod p ) i − 1 ≡ ( p − [ p i ] ) ∗ ( p mod i ) − 1 ( mod p )

由此我们就可以从前面推出当前的逆元了。

Code2 C o d e 2

#include
#include
#include
#include
#define MAXN 3000005
long long inv[MAXN];
int main() {
    int n, p;
    scanf("%d%d", &n, &p);
    inv[1] = 1;
    puts("1");
    for (int i = 2; i <= n; i++) {
        inv[i] = (p - p / i) * inv[p % i] % p; 
        printf("%lld\n", inv[i]);
    }
    return 0;
}

你可能感兴趣的:(数论,逆元,gcd,exgcd,c++,数论,gcd)