关于扩展GCD的梳理

最近做了一些关于扩展GCD的题,有点懵,现在有点头绪。就大致写写。

扩展GCD,即是算满足ax+by=gcd(a,b)该式的x,y的值。

而在一般解题过程中,往往需要变通。如:

在解ax+by=c时,需要先算出gcd(a,b)。然后判断c%gcd(a,b)==0是否成立。解的个数就是gcd(a,b)的个数。

个人理解如下:

ax+by=gcd(a,b)有解,而c%gcd(a,b)==0。所以c能整除gcd(a,b)。设c/gcd(a,b)=n。则ax+by=c可以化为ax+by=gcd(a,b)*n。再化为a*(x/n)+b*(y/n)=gcd(a,b)。所以恒有解。

然后是问题的具体解法:

1.判断c%gcd(a,b)

2.ax0+by0=gcd(a,b)已经根据扩展gcd公式算出。而需解a*(x/n)+b*(y/n)=gcd(a,b)。明显有x/n=x0,y/n=y0。所以解出x=x0*n y=y0*n ,即x=x0*(c/gcd(a,b)) 同理y=y=*(c/gcd(a,b))。

求解完成!

3.而题目一般会要求最小解。下面讨论如何求最小解:

我们设x通解为x0+t*n(x0为上面求得的x,t为正整数,c表示x解的间距)。同理y通解为y0+t*m。

所以有a*(x0+t*n)+b*(y0+t*m)=c恒成立。而a*x0+b*y0=c也是恒成立的。所以有a*t*n-b*t*m=0。因为我们需要通解t=0时x=x0,y=y0,所以当t!=0时,有a*n=b*m(a,b为已常数),所以要使n,m最小(因为n,m是x,y解的间距,需要最小),而又有解,需让a*n等于a,b的最小公倍数,b*m等于a,b的最小公倍数。

例:3*n=5*m 最小n,m分别是5,3。

而a,b最小公倍数为a*b/gcd(a,b)。所以解得n=b/gcd(a,b)  m=a/gcd(a,b)

最小解x=(x0%n + n)%n  y=(y0%m+m)%m  

x0%n使x落于区间(-n~n), +n使的解为正数,再次%n使得解最小。最小y同理。

4.解答完毕!

 

下面是扩展GCD的模版:

 

int ExGcd(int a, int b, int &x, int &y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	int t = ExGcd(b, a%b,x,y);
	int temp = x;
	x = y;//x1=y2
	y = temp - (a / b)*y;
	return t;
}

下面是对该式的说明:

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

2.以下式子基于递归性质:

要求ax1+by1=gcd(a,b)的解:

由题意等量代换有:bx2+(a%b)y2=gcd(b,a%b)

而a%b = a- (a/b)*b 

所以bx2+(a-(a/b)*b)*y2=gcd(b,a%b)=gcd(a,b)

整理得:ay2+b(x2-(a/b)*y2) = gcd(b,a%b) = gcd(a,b)

ax1+by1=gcd(a,b)。所以有x1=y2 , y1=x2-(a/b)*y2。

 

一个例题:

//题目内容:
//计算循环语句的执行频次 for (i = A; i != B; i += C) x += 1;
//其中A, B, C, i都是k位无符号整数。
//输入描述
//A B C k, 其中0<k<32
//
//	输出描述
//	输出执行频次数,如果是无穷,则输出“forever”
//
//	输入样例
//	3 7 2 16
//
//	输出样例
//	2
#include <iostream>
#include <cmath>
using namespace std;

typedef long long LL;

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

LL ex_gcd(LL a, LL b, LL &x, LL &y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		return a;
	}
	LL ans = ex_gcd(b, a%b, x, y);
	LL temp = x;
	x = y;
	y = temp - a / b*y;
	return ans;
}

int main()
{
	LL A, B, C, k;
	cin >> A >> B >> C >> k;

	LL a, x, b, y, n;
	a = C;b = pow(2, k);n = B - A;
	//原式转化为A+t*C=B (mod 2^k)  t为整数,为要求的数
	//t*c+y*2^k=B-A     x即t
	//继续转化  a=c,x=t,b=2^k,y=y,n=B-A
	//即ax+by=n;需求x
	LL temp = gcd(a, b);
	if (!A&& !B && !C)
	{
		cout << 0 << endl;
		return 0;
	}
	if (n%temp != 0 || temp==0)
	{
		cout << "forever" << endl;
		return 0;
	}
	ex_gcd(a, b, x, y);//求得x0,y0
	x = (n / temp)*x;//x为所得,但不一定为最小所得值
	LL T = b / temp;//T为区间
	x = ( x % T + T ) % T;//取得最小所得值
	cout << x << endl;
//	system("pause");
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值