乘法逆元总结

部分内容参考李煜东《算法竞赛进阶指南》。

定义:

若已知a,b,若a*c%b=1,则称c是a关于b的逆元。

应用范围:

有理数取余中国剩余定理,解同余方程等。

求法:

1.硬算
通过枚举来寻找逆元神一般的做法,一般没什么人用。
具体代码

int py(int a,int b){
    int c=1;
    while(1){
        if(a*c%b==1)
            return c;
        c++;
    }
}

2.费马小定理
若b为质数,有费马小定理知a^(b-1)%b=1,所以a的逆元为a^(p-2),配合快速幂做,缺点是只适用于b为质数。
具体代码

int power(int a,int b){
    int c,d=b+2;
    while(b){
        if(b%2)
            c=c*a%d;
        a=a*a%d;
        b/=2;
    }
    return c;
}
int py(int a,int b){
    return power(a,b-2);
}

3.扩展欧几里得定理

这算是用的最多的了,下面来说一下这种方法。
bezout定理:对于任意整数a,b,必存在一对整数x,y,使得a*x+b*y=gcd(a,b)。
证明
在gcd最后一步中(b=0),显然当x=1,y=0时成立。当b>0时,gcd(a,b)=gcd(b,a%b),设存在一对整数x,y满足bx+(a%b)y=gcd(b,a%b),因为bx+(a%b)y=bx+(a-b*(a/b))y=ay-b(x-(a/b)y),所以令x=y,y=x-(a/b)y,就证明完成。
而扩展gcd就是模拟bezout定理的过程,当证明中的a=a,b=1时,可求的逆元。
具体代码

//d为最大公约数
int exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1,y=0;
        return a;
    }
    int d=ecgcd(b,a%b,x,y);
    int z=x;
    x=y,y=z-y*(a/b);
    return d;
}
long long q(long long a,long long b)
{
    long long x,y;
    long long d=exgcd(a,b,x,y);
    if(d==1) return (x%b+b)%b;
    return -1;
}

4.欧拉定理:

a^(φ(b))%b=1(a与b互质)
这个适用于模数不是素数,且要与快速幂,线性筛连用,这个定理的核心是求出φ(),对于一个数m来说,在将m分解后可得m=a1^p1*a2*p2……an^pn,而φ(m)=m*(a1-1)/a1*(a2-1)/a2……*(an-1)/an。
具体实现

#include<bits/stdc++.h>
using namespace std;
int prime[200],v[1000],tot=0,num[1000];
void cl(int q){
    memset(v,0,sizeof(v));
    memset(prime,0,sizeof(prime));
    for(int i=2;i<=q;i++){
        if(!v[i]){
            v[i]=i;
            prime[++tot]=i;
        }
        for(int j=1;j<=tot;j++){
            if(prime[j]>v[i]||prime[j]>q/i)
                break;
            v[i*prime[j]]=prime[j];
        }
    }
}
void fj(int n){
    memset(num,0,sizeof(num));
    while(v[n]!=n){
        num[v[n]]++;
        n/=v[n];
    }
    num[n]++;
}
int power(int a,int b,int d){
    int c=1;
    cout<<d<<endl;
    while(b){
        if(b%2)
            c=c*a%d;
        a=a*a%d;
        b/=2;;
    }
    return c;
}
int py(int a,int b){
    int ans=b;
    for(int i=1;i<=tot;i++)
        if(num[prime[i]])
            ans=ans*(prime[i]-1)/prime[i];
    return power(a,ans-1,b);
}
int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    cl(b),fj(b);
    printf("%d",py(a,b));
    return 0;
}

一道乘法逆元板子题
代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
    if(a==0) {x=0;y=1;return b;}
    else
    {
        int tx,ty;
        int d=exgcd(b%a,a,tx,ty);
        x=ty-(b/a)*tx;
        y=tx;
        return d;
    }
}
int main()
{
    int n,p;scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        exgcd(i,p,x,y);
        while(x<0) x+=p;
        printf("%d\n",x);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sjzezwzy/article/details/81007030