逆元巨详细解法(欧拉定理,费马小定理,扩展欧几里德算法,递推,递归)

  ( 应acm协会的以往的习惯,每届学长学姐都要给下一届的学弟们(对,我们的下一届没有学妹)讲一个知识点,菜菜的我没什么别的选择,只能硬着头皮给他们讲数论基础,历时整整一天半,终于把课件以及要讲的东西捋清楚了TVT。虽然真正讲课的时候 垮了,但是自己也真的把这些平常看着头大的定理理清楚了!!!

好了进入正题:

一、欧几里德算法(这里直接供上ppt内容)

Baymax

二、扩展欧几里德算法

这个算法其实就是用来解二元一次方程的一组满足方程的(x,y)。主要的思想就是通过迭代来找解,每次的结果都作为下一次的初始值,直到达到满足条件为止。至于那个二元一次方程则为:ax+by=gcd(a,b)   (ax+by=z)

此处引入一个引理:裴蜀定理:若ax+by = z,则 gcd(a,b)| z 。所以求出x,y以后再乘上  \frac{z}{gcd(a,b)}就是括号里方程的解了

(式子看起来比较高级,其实也很简单,把gcd(a,b)|z 换成另一种形式  \frac{ax+by}{gcd(a,b)} 应该就好理解了)

接下来就是扩展欧几里德算法的具体实现:

Baymax

(这个图片上的内容其实就是从百度百科上复制下来的,因为挺好懂的也就没有再加什么注释了)

上代码:

//扩展欧几里得算法
long long  exgcd(long long  a,long long  b,long long  &x,long long  &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    long long  ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}

三、费马小定理(欧拉儿子定理)

费马小定理:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p )

(欧拉定理:对于任意互素的an,有a^φ(n) ≡1(mod n ) 其中φ(n)指的是1~n中与n互质的数的个数,以下是对欧拉定理的理解)

Baymax


几个与逆元相关的定理到这就讲完了,接下来就进入正题!!!

逆元:(单纯理解就是一个数的倒数 a*b=1 b就是a的倒数 不过不能这么去记,貌似这样是不对的,想要科学官方的解释可以去百度百科搜)它是用来解决(a/b)%m (a很大,b也很大)这样的问题。由于带了除的模运算并没有对应的运算规则,所以就将其转换成 (a*b')%m=((a%m)*(b'%m))%m 这里b'是b在mod m意义下的逆元。不过要注意:只有当b和m互质的时候才存在逆元。

那么逆元又应该怎么求呢???

方法一:扩展欧几里德算法    

原理a∗b≡1(mod p)  =>  a∗b+k∗p=1  (把b看做x,k看做y,将此二元一次方程解出来一个解就好)

//求a在mod下的逆元,不存在逆元返回-1
long long  getInv1(int a,int mod){
    long long  x,y;
    long long  d=exgcd(a,mod,x,y);//上面提到的扩展欧几里德算法
    return d==1?(x%mod+mod)%mod:-1;//防止x小于0
}

方法二:费马小定理

              由费马小定理有  a^(p−1)≡1(mod p)   =>   a^(p−2)∗a≡1(mod p)   => a^(p−2)就是amod p意义下的逆元

//快速幂求a的p次方 (mod m)
long long qkpow(long long a,long long p,long long m)
{
    long long t=1,tt=a%m;
    while(p)
    {
        if(p&1)t=t*tt%m;
        tt=tt*tt%m;
        p>>=1;
    }
    return t;
}
long long getInv2(long long a,long long mod)
{
    return qkpow(a,mod-2,mod);
}

方法三:递归或 递推 (其中递推又称逆元的线性解法)

//求a关于p的逆元,注意:a要小于p,最好传参前先把a%p一下
long long getInv3(long long a, long long p) {
  return a == 1 ? 1 : (p - p / a) * getInv3(p % a, p) % p;
}

long long inv[9978];
void getInv4(long long mod)
{
    inv[1]=1;
    for(int i=2;i<mod;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
//这里用的公式我并不是很懂,如果哪位大佬知道的话
//可以指教一下 OVO

好了到这就结束了,希望能给大家带来帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值