乘法逆元的学习总结

以下内容均为牌王(可点开链接哦)所教,此人无敌!!!感谢牌王的细心教导,嘿嘿嘿

逆元的定义

逆元可以说是倒数概念的推广,对于正整数a,p,若有ax≡1(mod p),则称x是a关于模p的逆元。例如:
在这里插入图片描述
那么为什么要有逆元呢?逆元有什么作用?
加减乘与模运算的顺序交换不会影响结果,即可用分配律,但是除法呢?答案是不行的,那么有什么办法可以完成除法与模运算之间的运算呢,这时候就是主角逆元登场了。
例如求解 ( a / b ) m o d p (a / b) mod p (a/b)modp
先求出b关于p的逆元x,即 b ∗ x ≡ 1 ( m o d    p ) b*x≡1(mod\; p) bx1(modp),将其带入上式得
( a b ∗ b ∗ x ) ≡ 1 ( m o d p ) − > ( a ∗ x ) ≡ 1 ( m o d    p ) (\frac{a}{b} * b * x)≡ 1 (mod p) -> (a * x)≡1(mod\; p) (babx)1(modp)>(ax)1(modp)
即可将除法转化成乘法,是不是很有趣?
逆元存在的条件:a与p互质,即gcd(a,p)=1

求解逆元的三种方法

  1. 费马小定理
    当p为素数时,则有
    在这里插入图片描述
    将其变形为
    在这里插入图片描述
    由逆元定义可知, a p − 2 a^{p-2} ap2就是逆元

代码如下:

ll FPM(ll x,ll power,ll mod)
{
    ll ans = 1;
    while(power)
    {
        if(power & 1)
        {
            ans = (ans * x) % mod;
        }
        x = (x * x) % mod;
        power >>= 1;
    }
    return ans % mod;
}
  1. 扩展欧几里得算法

ax≡1(mod p)->ax = pk + 1
当a,p互质时,gcd(a,b)=1,即将上式转化为
在这里插入图片描述
当求解gcd(a,b)时,递归结束的条件为b=0,得出a=gcd(a,b),进一步得出x=1,所以对上式使用递归时,结束条件为b = 0,并使x=1,y=0。

if(!b)
{
	x = 1;
	y = 0;
	return a;
}

紧接着一直回朔,最后得出x的值,并且还可以求出a和b的最大公约数,是不是一举两得?

回朔过程的解析:
a ∗ x 1 + b ∗ y 1 = g c d ( a , b ) a*x1+b*y1=gcd(a,b) ax1+by1=gcd(a,b)
a ∗ x 2 + b ∗ y 2 = g c d ( a , b ) a*x2+b*y2=gcd(a,b) ax2+by2=gcd(a,b)
将第二个式子变化一下为
b x 2 + ( a − a / b ∗ b ) ∗ y 2 = g c d ( a , b ) − > a y 2 + b ( x 2 − a / b ∗ y 2 ) = g c d ( a , b ) bx2+(a-a/b*b)*y2=gcd(a,b)->ay2+b(x2-a/b*y2)=gcd(a,b) bx2+(aa/bb)y2=gcd(a,b)>ay2+b(x2a/by2)=gcd(a,b)
x 1 = y 2 , y 1 = x 2 − a / b ∗ y 2 x1=y2, y1=x2-a/b*y2 x1=y2,y1=x2a/by2
那么不断回朔回去,即可得到x。

得出 x x x后,如果为负数,则需要 ( x % m o d + m o d ) % m o d (x\% mod + mod) \% mod (x%mod+mod)%mod使其变为正

当然,可以将其推广则可求出任意二元一次方程的最小解,不过也是有条件的,例如
求ax+by=c的解,当且仅当 c 是 g c d ( a , b ) c是gcd(a,b) cgcd(ab)的倍数时, x x x有最小解。

代码如下:

typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y) //扩展欧几里得算法
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    ll ret = exgcd(b, a % b, x, y);
    ll t = x;
    x = y;
    y = t - a / b * x;
    return ret;
}
ll inv(int a, int mod)  //求a在mod下的逆元,如果不存在返回-1
{
    ll x, y;
    ll d = exgcd(a, mod, x, y);
    return d ? (x % mod + mod) % mod : -1;
}
  1. 线性递推

过程如下:
在这里插入图片描述
这里就不再详细描述了…直接上代码!!!(打字太累了)

代码如下:

ll inv[3000005] = {0 , 1}; //n等于1时,关于mod的逆元就为1
int main()
{
    ll n, mod;
    scanf("%lld%lld",&n,&mod);
    printf("1\n");
    for(int i = 2;i <= n; i++) // 从二开始,防止改变inv[1]的值
    {
        inv[i] = mod - (mod / i) * inv[mod % i] % mod;// 这一步important // 加个mod是为了防止逆元为负
        printf("%lld\n",inv[i]);
    }
    return 0;
}

狙击美佐第一篇博客的感想

很早就想自己写点东西记录自己的学习过程,可以太懒了,而且语言组织也不好,不过,在数论方面,每次我遇到问题,牌王都会帮我解答,非常感谢牌王!!!那么这次我就将这篇博客写好,“送”给你来表达我对你的爱?

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值