扩展欧几里得 除法逆元

本文介绍了欧几里得算法及其拓展形式,用于计算两个整数的最大公约数(GCD)以及解决线性同余方程。详细阐述了算法原理,并提供了C++代码实现,包括最大公约数、最小公倍数和求解模的逆元。此外,讨论了在模运算中求逆元的三种方法,包括费马小定理、扩展欧几里得算法和线性递推。这些算法在计算数学和信息技术领域有广泛应用。
摘要由CSDN通过智能技术生成

欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数

定理:gcd(a,b)==gcd(b,a%b) 

附带可能会用到的:gcd(a,b)==gcd(a,a-b)==gcd(a,a+b)  gcd(a,b,c)==gcd(a,b-a,c-b)

int gcd(int a, int b)//最大公约数
{
    return b == 0 ? a : gcd(b, a % b);
}

int lcm(int a, int b)//最小公倍数
{
    return a / gcd(a, b) * b;
}

扩展欧几里得算法,简称 exgcd,一般用来求解不定方程,求解线性同余方程,求解模的逆元等

引理:存在 x , y 使得 gcd(a,b)=ax+by

 当 b=0 时,gcd(a,b)=a,此时 x2= 1, y2=0 因此只要从此处往前处理,在进行欧几里德算法的递归的时候根据相邻两次调用间x1,y1和x2,y2的关系 x1=y2 , y1=x2-a/b*y2 计算即可求解ax+by=gcd(a,b)的解.

int exgcd(int a, int b, int&x, int&y)    //拓展欧几里得算法
{
    if(!b) 
       x = 1, y = 0;
       return a;
    else
    {
       int ans=exgcd(b, a % b, x, y);
	   int tmp=x;
	   x=y;
	   y=tmp-a/b*y;
       return ans;
    }
}

 对于 ax+by=c 的不定方程,设 r=gcd(a,b) 

  当 c%r!=0 时无整数解

  当 c%r=0 时,将方程右边 *r/c 后转换为 ax+by=r 的形式

  可以根据扩展欧几里得算法求得一组整数解 x0 , y0

  而这只是转换后的方程的解,原方程的一组解应再 *c/r 转变回去

  则原方程解为 x1=x0*c/r , y1=y0*(c/r);

  通解 x=x1+b/r*t , y=y1-a/r*t ,其中 t 为整数

ll x,y,r,s;
void exgcd(ll a, ll b, ll &x, ll &y)    //拓展欧几里得算法
{
    if(!b) 
        x = 1, y = 0;
    else
    {
        exgcd(b, a % b, y, x);
        y -= x * (a / b);
    }
}

ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
void BD(ll a,ll b,ll c,ll r)
{
    exgcd(a,b,x,y);
    x=x*c/r;//得到原方程的解x和y
    y=y*c/r;
}

    int n,k1,k2;
    cin>>n>>k1>>k2;
    //k1*x+k2*y==n
	int x=0,y=0;
	int gcd=exgcd(x,y,k1,k2);
	int modx=abs(y/gcd);
	int mody=abs(x/gcd);
	x=(x%modx+modx)%modx;  //x的最小整数解
	y=(y%mody+mody)%mody;  //y的最小整数解


 

#include<iostream>
using namespace std;
#define int long long
int x,y,n,m,l;
int a,b,c;
int exgcd(int&x,int&y,int a,int b){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	int ans=exgcd(x,y,b,a%b);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return ans;
}
signed main(){
	cin>>x>>y>>m>>n>>l;
	a=n-m;
	b=l;
	c=x-y;
	x=0;y=0;
	int gcd=exgcd(x,y,a,b);
	if(c%gcd==0){
		x=x*c/gcd;
		if(gcd<0) gcd=-1*gcd;
		if(x<0){
			while(x<0) x+=b/gcd;
		}
		else{
			while(x>0&&x-b/gcd>0) x-=b/gcd;
		}
		cout<<x<<endl;
	}
	else{
		cout<<"Impossible"<<endl;
	}
}

除法逆元的三种求法:

逆元用处:(A/B)%p!=((A%p)/(B%p))%p 求 (A / B) %p ;在B的值非常大的情况下,B作为除数,极有可能会爆精度,除数不能太大;所以我们可以把他转化为乘法来解决

逆元存在条件: 求a关于p的逆元,逆元存在要求a和p互质

\frac{a}{b}\, mod\: p\equiv a*inv[b]inv[b]=\frac{1}{b}mod \, p 所以 b*inv[b]=1mod p 所以求解b的逆元inv[b]即求解b*inv[b]=1mod p

1.费马小定理 要求 1.a与p互质 2.p是质数 

 inv(a)=a^(p-2) mod p

LL ksm(LL a, int x) {
	LL ans = 1;
	while(x) {
		if(x&1) ans = (ans * a) %mod;
		a = (a * a) %mod;
		x >>= 1;
	}
	return ans;
} 

LL inv(LL a) {
	return ksm(a, mod - 2);
}

2.扩展欧几里得求逆元 要求 1.a与p互质 (比费马小定理求解要快)

ax ≡ 1(mod p) => a*x+p*y = 1 求解出x即可 如果gcd不为1则说明a与p不互质不存在逆元

typedef  long long ll;
void extgcd(ll a,ll b,ll& d,ll& x,ll& y){
    if(!b){ d=a; x=1; y=0;}
    else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); }
}
ll inverse(ll a,ll n){
    ll d,x,y;
    extgcd(a,n,d,x,y);
    return d==1?(x%n+n)%n:-1; //最小整数解
}

3. 有时会遇到这样一种问题,在模质数p下,求1~n逆元 n< p(这里为奇质数)。可以O(n)求出所有逆元,有一个递推式如下

当p是个质数的时候有 inv(a) = (p - p / a) * inv(p % a) % p

typedef  long long ll;
const int N = 1e5 + 5;
int inv[N];
 
void inverse(int n, int p) {
    inv[1] = 1;
    for (int i=2; i<=n; ++i) {
        inv[i] = (ll) (p - p / i) * inv[p%i] % p;
    }
}

求解C(n,m) 采用打表把1-n的阶乘关于p的逆元求出来,要求1-n的阶乘关于p的逆元,可以先把1-n关于p的逆元求出来

C(n,m)==n!/m!/(n-m)!=n!*inv[m!]*inv[(n-m)!]

int fac[maxn];
int inv[maxn];
void init(int n){//逆元打表
	fac[0]=1;
	for(int i=1;i<=n;i++){//阶乘
		fac[i]=fac[i-1]*i%mod;
	}
    inv[0]=1;
	inv[1]=1;
	for(int i=2;i<=n;i++){//1~n在 M 下的逆元
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=2;i<=n;i++){//1~n的阶乘在 M 下的逆元
		inv[i]=inv[i]*inv[i-1]%mod;
	}
}
int comb(int a,int b){//求组合数
	if(b==0){
		return 1;
	}
	if(b<0||b>a){
		return 0;
	}
	if(a==b){
		return 1;
	}
	return fac[a]*inv[b]%mod*inv[a-b]%mod;//组合数公式
}

求解A(n,m) A(n,m)=n!/(n-m)!=n!*inv[(n-m)!]

int fac[maxn];
int inv[maxn];
void init(int n){//逆元打表
	fac[0]=1;
	for(int i=1;i<=n;i++){//阶乘
		fac[i]=fac[i-1]*i%mod;
	}
    inv[0]=1;
	inv[1]=1;
	for(int i=2;i<=n;i++){//1~n在 M 下的逆元
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=2;i<=n;i++){//1~n的阶乘在 M 下的逆元
		inv[i]=inv[i]*inv[i-1]%mod;
	}
}
int aomb(int a,int b){//求组合数
	if(b==0) return 1;
	if(b<0||b>a) return 0;
    if(a==b) return fac[a];
	return fac[a]*inv[a-b]%mod;//组合数公式
}

​

一个关于减法余数的易错点 (A-B)%mod==(A%mod-B%mod+mod)%mod

C(n,m)=n!*inv[m!]*inv[(n-m)!] 还可以采用线性递推的方法 C(n,m)=C(n-1,m)+C(n-1,m-1)

从n个物品中取m个有2种情况:(1)不取第n个物品,于是从前n-1个中取m个; (2)取第n个物品,于是从前n-1个中取m-1; 所以答案是这两种情况的和

int c[100][100];
void init(){
	for(int i=0;i<=65;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++){
			c[i][j]=c[i-1][j-1]+c[i-1][j];
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值