Loading

辗转相除法及扩展欧几里得算法

1辗转相除法

1.1

辗转相除法多用于求最大公约数,其算法的核心在于做有限次带余除法,最大公约数的值不变,即\(gcd(a,b)=gcd(b,a-a/b*b)\)(即a%b)
证明: 如果z是a和b的公因子,即z整除a,z整除b,则z必然整除a-b,所以z是a-b的因子,所以z是a,b,a-b的公因子
如果z不是a的因子,那么即使z是b的因子,z也不是a和b的公因子,z不是b的因子更不用提
如果z不是b的因子,同上可得z不是a和b的公因子。
所以a和b的公因子和a和a-b,b和a-b的公因子一样,则最大公因数也一样,所以\(gcd(a,b)=gcd(b,a-b)\),所以\(gcd(a,b)=gcd(b,a-a/b*b)\)
a能整除b时,b即为最大公约数,这时a%b等于0

1.2算法实现

inline int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

2扩展欧几里得算法

2.1

根据贝祖定理,ax+by=c的充分必要条件是c整除gcd(a,b)
证明:
可以得到\(ax+by=gcd(a,b)=gcd(b,a-a/b*b)=b*x_0+(a-a/b*b)*y_0=a*y_0+b*(x_0-a/b*y_0)\)
根据上面这个式子,可以得到:根据辗转相除,a-a/b*b最终会变成0,而这时x_0等于1,y_0等于0明显符合上式,原式答案可以经过下面的递归式算出,同样也是根据上面这个式子:
\(x=y_0\)
\(y=x_0-a/b*y_0\)
同时,这说明一定存在整数x,y,满足方程ax+by=gcd(a,b),故有c必须被gcd(a,b)整除。
再取得特解后,发现\(x=x_0-b/gcd(a,b)*t,y=y_0+a/gcd(a,b)*t\)中x,y也满足原方程,所以我们得到了上面方程的通解

2.2算法实现

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

2.3辗转相除法的二进制算法

其算法核心在于除去因子2以降低常数,提高速度
核心:

  1. 若a、b都为偶数,那么\(gcd(a,b)=2*gcd(a/2,b/2)\)
  2. 若a为偶数,b为奇数,\(gcd(a,b)=gcd(a/2,b)\)
  3. 若a为奇数,b为偶数,\(gcd(a,b)=gcd(a,b/2)\)
  4. 若a、b都为奇数,\(gcd(a,b)=gcd(b,a-b)\)

2.4代码

inline int gcd(int a,int b){
	int i,j;
	if(a==0) return b;
	if(b==0) return a;
	for(i=1;(a&1)==0;i++) a>>=1;
	for(j=1;(b&1)==0;j++) b>>=1;
	if(j<i) i=j;
	while(1){
		if(a<b) a^=b,b^=a,a^=b;//swap(a,b)
		if(0==(a-=b)) return b<<i;
		while(a&1==0) a>>=1;
	}
}

3 题目P1516青蛙的约会

https://www.luogu.com.cn/problem/P1516
提示:把题目代入数学模型,发现是一个不定方程,由此由扩展欧几里得算法得到一组特解,再求最小正整数解时,对它进行取模运算,同时不要忘了负数的处理。
由于总是快的去追慢的,所以要注意n-m的正负。

3.1代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

ll x,y,m,n,l;

inline int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

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

int main(){
	scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l);
	ll t,p,a=n-m,b=x-y;
	ll gcd=exgcd(l,a,p,t);
	if(b%gcd!=0){
		printf("Impossible\n");
		return 0;
	}
	printf("%lld",(b/gcd*t%(l/gcd)+l/gcd)%(l/gcd));
	return 0;
}
posted @ 2021-01-25 15:36  hyl天梦  阅读(314)  评论(0编辑  收藏  举报