扩展欧几里得算法(求逆元)

L1 前论

1. 欧几里得算法
欧几里德算法又称辗转相除法,用于计算两个整数 a , b a,b a,b的最大公约数。

g c d gcd gcd函数的基本性质
g c d ( a , b ) = g c d ( b , a ) = g c d ( − a , b ) = g c d ( ∣ a ∣ , ∣ b ∣ ) gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|) gcd(a,b)=gcd(b,a)=gcd(a,b)=gcd(a,b)

公式表述
g c d ( a , b ) = g c d ( b , a    m o d    b ) gcd(a,b)=gcd(b,a\;mod\;b) gcd(a,b)=gcd(b,amodb)

L2 扩展算法

对于不完全为 0 的非负整数 a , b , g c d ( a , b ) a,b,gcd(a,b) abgcdab表示 a , b a,b ab 的最大公约数,必然存在整数对 x , y x,y xy,使得 g c d ( a , b ) = a x + b y gcd(a,b)=ax+by gcdab=ax+by

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

g c d ( a , b ) gcd(a,b) gcd(a,b)函数一样,扩展 g c d gcd gcd也是通过递归的思想来实现。在 g c d ( a , b , x , y ) gcd(a,b,x,y) gcd(a,b,x,y) b ! = 0 b!=0 b!=0时,会运算函数 g c d ( b , a % b , y , x ) gcd(b,a\%b,y,x) gcd(b,a%b,y,x),直到b=0时函数返回此时a的值

对于x,y的赋值才是这个函数的精髓

L3 算法证明

不妨先设a>b

在b=0时,显然gcd(a,b)=a。此时 x=1,y=0;

a>b>0 时


ax1+ by1= gcd(a,b); //原函数gcd(a,b,x,y)
bx2+ (a mod b)y2= gcd(b,a mod b); //第一次递归的函数gcd(b,a%b,y,x)

欧几里德原理有 gcd(a,b) = gcd(b,a mod b);
ax1+ by1= bx2+ (a mod b)y2

而对于mod运算,a-[a/b]*b即为a mod b。[a/b]代表取小于a/b的最大整数

mod运算代入原等式得:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2
化简得:ax1+ by1 == ay2+ b(x2- [a / b] *y2);
此时便有恒等式:x1=y2; y1=x2- [a / b] *y2;

已经很明显了,x1,y1 的值基于 x2,y2,当递归到b==0时,对x,y赋值为1,0,然后一层一层用恒等式改变x和y的值

L4 算法讲解

和欧几里得一样,扩展欧几里得函数返回的是gcd(a,b),此时的x和y带入原方程ax+by等的是gcd(a,b)

要想等的是原方程的c,那么gcd(a,b)要变成c,也就是如下方程:
a ∗ x ∗ c g c d ( a , b ) + b ∗ y ∗ c g c d ( a , b ) = g c d ( a , b ) ∗ c g c d ( a , b ) a*x*\dfrac{c}{gcd(a,b)}+b*y*\dfrac{c}{gcd(a,b)}=gcd(a,b)*\dfrac{c}{gcd(a,b)} axgcd(a,b)c+bygcd(a,b)c=gcd(a,b)gcd(a,b)c
所以当c不能整除gcd(a,b)时, x ∗ c g c d ( a , b ) x*\dfrac{c}{gcd(a,b)} xgcd(a,b)c在数学的角度并不是一个整数,即, a ∗ x + b ∗ x = c a*x+b*x=c ax+bx=c 无整数解

L5 解的情况

这里写图片描述
可知,如果 a x + b y = c ax+by=c ax+by=c有解,那么一定是无限组解

设有任意的一组解为 ( x , y ) (x,y) (x,y),那么 ( x + k ∗ b / g c d ( a , b ) , y − k ∗ a / g c d ( a , b ) ) (x+k*b/gcd(a,b),y-k*a/gcd(a,b)) (x+kb/gcd(a,b),yka/gcd(a,b))也是这个方程的解,这个便是我们求某种特殊解的前提

假设题目要求的是x的最小非负整数解,那么我们用扩展欧几里得求出来的x,先乘上 c g c d ( a , b ) \dfrac{c}{gcd(a,b)} gcd(a,b)c使之变成ax+by=c的解,再通过一下操作拉到 [ 0 , b / g c d ( a , b ) ] [0,b/gcd(a,b)] [0,b/gcd(a,b)]范围
令 d = b / g c d ( a , b ) x = ( x % d + d ) % d 令d=b/gcd(a,b)\\x=(x\%d+d)\%d d=b/gcd(a,b)x=(x%d+d)%d

当然如果求的是最小正整数解,只需要在x为0的时候变成b/gcd(a,b)就可以了

L5 模板(x为最小非负整数解)

/*
 *  Author : Jk_Chen
 *    Date : 2019-08-21-10.26.44
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
#define debug(x) cerr<<#x<<" = "<<x<<'\n';
const LL mod=1e9+7;
const int maxn=1e5+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

LL Ex_gcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        y=0,x=1;
        return a;
    }
    LL ans=Ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return ans;
}
LL MinNotNeg(LL a,LL b,LL c){
    if(!a&&!b){
        return -(c!=0);
    }
    LL x,y;
    LL G=Ex_gcd(a,b,x,y);
    if(c%G)return -1;
    x*=c/G; y*=c/G;
    b=abs(b/G);
    return (x%b+b)%b;
}

int main(){
    cout<<MinNotNeg(5,7,12)<<endl;
    return 0;
}

求逆元

这里写图片描述


青蛙的约会

链接:zjnu 1439

题意大致为
在一首位相接的长为L的数轴上,A从坐标x出发,每次往正方向走m,B从坐标y出发,每次往正方向走n,问能不能在某一次跳跃后落在同一点,如果能,输出跳跃次数,如果不能,输出“Impossible”。

分析
经过t次跳跃,A坐标为 ( x + ( t ∗ m ) ) (x+(t*m))%L (x+(tm)),B坐标为 ( y + ( t ∗ n ) ) (y+(t*n))%L (y+(tn)),当存在t使两者相同时,此t便是ans。

( x + t ∗ m ) % L − ( y + t ∗ n ) % L = 0 (x+t*m)\%L - (y+t*n)\%L = 0 (x+tm)%L(y+tn)%L=0
( ( x + t ∗ m ) − ( y + t ∗ n ) ) % L = 0 ( (x+t*m)-(y+t*n) )\%L = 0 ((x+tm)(y+tn))%L=0
( x − y ) + ( m − n ) ∗ t = k ∗ L (x-y)+(m-n)*t = k*L (xy)+(mn)t=kL
L ∗ k + ( n − m ) ∗ t = x − y L*k+(n-m)*t=x-y Lk+(nm)t=xy
a ∗ X ′ + b ∗ Y ′ = c a*X&#x27;+b*Y&#x27;=c aX+bY=c
( L = a ; n − m = b ; x − y = c ; X ′ = k ; Y ′ = t ) (L=a ; n-m=b ; x-y=c ; X&#x27;=k ; Y&#x27;=t) (L=a;nm=b;xy=c;X=k;Y=t)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define D long long
using namespace std;

D gcd(D a,D b,D &x,D &y){
    if (b==0){
        x=1,y=0;
        return a;
    }
    D q=gcd(b,a%b,y,x);
    y-=a/b*x;
    return q;
}

int main()
{
	
	
	D x,y,n,m,L;while(cin>>x>>y>>m>>n>>L){
		D a,b,c;a=L;b=n-m;c=x-y;
		D X,Y;

		//a*X+b*Y=c
		D mid=gcd(a,b,X,Y);//此时赋值后的的Y是 Y'*gcd(a,b)/c (Y'是最后的解)  
		
		//cout<<X<<' '<<Y<<endl; 
		
		D ans=Y*c/mid;
		
		D mul=a/mid;//Y'的可变化幅度,(二元一次一定有多个解) 
		mul=mul<0?-mul:mul;//mul为负数时,答案可能为负 
		
		ans=(ans%mul+mul)%mul;//把ans拉回了[0,mul) 
		
		if(c%mid!=0||n==m)cout<<"Impossible"<<endl;
		else cout<<ans<<endl;
	}
	
}
欧几里得算法是一个用于计算两个整数的最大公约数的算法,扩展欧几里得算法可以在得最大公约数的同时计算出满足贝祖等式 ax + by = gcd(a,b) 的整数解 x 和 y,其中 a 和 b 是输入的整数。 扩展欧几里得算法可用于解模反元素(逆元),其中逆元是指某个整数关于模数的乘法逆元素。 下面是我用C语言实现扩展欧几里得算法逆元的示例代码: ``` #include <stdio.h> int extended_gcd(int a, int b, int *x, int *y) { if (b == 0) { *x = 1; *y = 0; return a; } int x1, y1; int gcd = extended_gcd(b, a % b, &x1, &y1); *x = y1; *y = x1 - a / b * y1; return gcd; } int mod_inverse(int a, int m) { int x, y; int gcd = extended_gcd(a, m, &x, &y); if (gcd != 1) { printf("逆元不存在\n"); return -1; // 逆元不存在 } int inverse = (x % m + m) % m; return inverse; } int main() { int a, m; printf("请输入要逆元的整数a和模数m:"); scanf("%d %d", &a, &m); int inverse = mod_inverse(a, m); if (inverse != -1) { printf("%d关于模数%d的逆元是:%d\n", a, m, inverse); } return 0; } ``` 这是一个简单的扩展欧几里得算法逆元的实现,首先通过`extended_gcd`函数出`a`和`m`的最大公约数,并计算满足贝祖等式的整数解`x`和`y`。如果最大公约数不为1,则逆元不存在。若最大公约数为1,则通过模的方式计算`x`关于模数`m`的逆元。代码中的`mod_inverse`函数用于调用`extended_gcd`函数,并处理逆元不存在的情况。最后,通过用户输入需要逆元的整数`a`和模数`m`,并输出结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值