扩展欧几里得算法与乘法逆元

Part 1:前置知识

  • 欧几里得算法

    a,bN,gcd(a,b)=gcd(b,amodb)

  • Bézout 定理

    对于任意整数 a,b,存在一对整数 x,y,满足 ax+by=gcd(a,b)

    证明:

    在欧几里得算法的最后一步,即 b=0 时,显然有一对整数 x=1,y=0,使得 a1+00=gcd(a,0)

    b>0,则 gcd(a,b)=gcd(b,amodb)

    假设存在一对正整数 x,y,满足 bx1+(amodb)y1=gcd(b,amodb)

    则有 ax+by=bx1+(amodb)y1

    ax+by=bx1+(aa/bb)y1

    ax+by=bx1+ay1(a/bb)y1

    ax+by=ay1+b(x1a/by1)

    x=y1,y=x1a/by1

    对欧几里得算法的递归过程应用数学归纳法,可知 Bézout 定理 成立

    利用 Bézout 定理,我们就可以使用 扩展欧几里得算法 来求出整数 x,y 的一组特解

Part 2:扩展欧几里得算法

1、求 ax+by=gcd(a,b) 的一组特解

  • 代码
#define LL long long
LL exgcd(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1;  y=0;  
		return a;
	}
		
	LL d=exgcd(b,a%b,x,y);
	LL tmp=x;
	x=y;  y=tmp-(a/b)*y;
		
	return d;
}

2.求 ax+by=gcd(a,b) 的通解

  • 已知 x0,y0 是一组特解,d=gcd(a,b)(下面的 d 含义相同)

  • 那么方程通解为

    x=x0+kbd,y=y0kad(kZ)

    证明:
    x,y 是除 x0,y0 之外的一组解

    则可得:{ax0+by0=d(1)ax+by=d(2)

    (2)(1) 得:a(xx0)=b(y0y)=lcm(a,b)k

    a(xx0)=lcm(a,b)k 进行分析:

    a(xx0)=abdk

    xx0=bdk

    x=x0+kbd

    b(y0y)=lcm(a,b)k 分析,同理可得:y=y0kad

3.求 ax+by=c 的解

  • 对于一般的方程 ax+by=c,它有解当且仅当 dc

  • 通过先前的扩欧,我们可以求出方程 ax+by=d) 的一组特解 x0,y0,然后令 x0,y0 同时乘上 c/d,就得到了 ax+by=c 的一组特解:(c/d)x0,(c/d)y0

  • 由于 cd 的倍数,所以类比求 ax+by=gcd(a,b) 的通解的过程,我们可以将ax+by=c 的通解表示为:

    x=cdx0+kbd,y=cdy0kad(k\Z)

Part 3:乘法逆元

1. 定义

  • 若整数 a,m 互质,且 ax1(modm),那么就称 xa 的模 m 乘法逆元。(记为 a1

2.扩展欧几里得算法求逆元

  • ax1(modm) 变形可得:axmy=1 (mZ)

  • 使用扩欧可轻松求出 x 的一个特解 x0

  • 若要求最小最小正整数解,则令 x=(x0modm+m)modm

  • 代码

void exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;  y=0;
		return;
	}
	
	exgcd(b,a%b,x,y);
	int tmp=x;
	x=y;  y=tmp-(a/b)*y;
	return;
}

int main()
{
	scanf("%d%d",&n,&m); //n为逆元表达式中的a
	
	int x=0,y=0;
	exgcd(n,m,x,y);
	x=(x%m+m)%m;
		
	printf("%d\n",x);
	
	return 0;
}

3.费马小定理求逆元

  • 前提条件:当 m 为质数时才可使用

  • 前置知识:费马小定理

    p 是质数,a 是正整数,且 a 不是 p 的倍数,则 ap11(modp)

  • 已知{ax1(modm)(1)am11(modm)(2),那么由 (1)(2)可推出 axam1(modm),所以 x=am2

  • 一般情况下,我们可以使用快速幂来求出 am2

4.线性递推求逆元

  • 当题目需要我们求出一串数字 mod m 的逆元时,我们会采用这种方式

  • 我们记 x 的逆元为 inv[x],已知 111(modm),所以 inv[1]=1

  • m=kx+r(1r<x<m),也就是说 km/x 的商,r 是余数。那么就有:

    kx+r0(modm)

    乘上 inv[x]inv[r],就有:

    kinv[r]+inv[x]0(modm)

    inv[x]kinv[r](modm)

    inv[x]mxinv[mmodx](modm)

  • 这样,我们就可以线性地递推出乘法逆元。但值得注意的是,一般题目中都要保证求出的逆元是正数,所以我们会在递推时往递推式中加一个 minv[mmodx]

  • 代码

inv[1]=1;
for(int i=2; i<=n; i++)
		inv[i]=(long long)(m-m/i)*inv[m%i]%m;
posted @   xishanmeigao  阅读(56)  评论(0编辑  收藏  举报
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示