拓展欧几里德算法笔记

1.定义

扩展欧几里德算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数)。通常谈到最大公因子时, 我们都会提到一个非常基本的事实: 给予二整数 a 与 b, 必存在有整数 x 与 y 使得ax + by = gcd(a,b)。我们求的就是x和y的所有解

2.证明过程

首先我们知道gcd(a, b)= gcd(b, a%b) -------①
然后我们写出下面的方程:
ax1 + by1 = gcd(a, b)

通过已知可向下推导一步得到:
bx2 + (a%b)y2 = gcd(b, a%b)

由①可得:
ax1 + by1 = bx2 + (a%b)y2

又因a%b = a - (a/b)b
带入上面式子得到:
ax1 + by1 = bx2 + [a - (a/b)b] y2
整理后得到
ax1 + by1 = ay2 + b(x2 - (a/b)y2)
由待定系数法得到
x1 = y2
y1 = x2 - (a/b)y2

这也就找到了两个式子之间的关系,那看到这很多人就不明白了,证得二者关系又有什么用呢?
接着看下去:
我们知道 gcd(a, b)在经过多次 gcd(b, a%b) 后a%b 是一定等于0的,此时对于式子
bx2 + a%by2 = gcd(b, a%b)就变为了bx = gcd(b,a%b ),我们又知道 b = gcd(b,a%b ) 所以得出x = 1
为了求得最小的解,我们可是让y = 0。
由此我们得到:
x2 = 1
y2 = 0

我们又通过上面知道原式和转换式之间x 和 y的关系,我们只要不断的回带回去,就能求出原式的一个特解。

下面贴出代码:
代码是先求出gcd(a, b), 然后利用上面结论,给x赋值1,y赋值0,然后进行回溯求原式特解。

void exgcd(int a, int b, int &gcd, int &x, int &y)
{
	if (b == 0)
	{
		x = 1;
		y = 0;
		gcd = a;
	}
	else
	{
		//这里x 和 y 换位就是利用了上面的证明出的结果的结论
		edgcd(b, a%b, gcd, y, x);
		//也可以写为
		//edgcd(b, a%b, gcd, x, y);
		//int t = x; x = y; y = t-(a/b)*y;
		//回溯求特解
		y -= (a/b)*x;
	}
} 

3.求通解

对于ax + by = gcd(a,b ) 而言,我们不难想象它的解是无穷的,上面我们已经求得一个特解,那我们怎么能知道所有的解呢?
特别的简单,对于求得的特解,我们给x不断的加上b/gcd(a, b),给y 不断的减去a/gcd(a, b)。什么意思呢?
很简单 带一下就明白了 x -> x + b/gcd(a, b), y -> y - a/gcd(a, b)
带入后得到 a(x + b/gcd(a, b)) + b(y - a/gcd(a, b)) 化简后可以看到式子并没有变化,还是等于gcd(a, b)。由此可以求得通解,可能有人就不太理解了,为什么一定写为b/gcd 直接写b不行嘛,如果直接是b每次加上的都是b,会落下很多解,所以一定得把b缩到最小,而最小就是除以和a的最大公约数gcd,因此得来。

代码如下(exgcd代码在上面,这里就不打了)

void General_Solution(int a, int b)
{
	int x, y, gcd;
	int n = 0;
	
	exgcd(a, b, gcd, x, y);
	
	while (1)
	{
		cout << a << "*" << x+n*b/gcd << "+" << b << "*" << y - n*a/gcd << " = " << gcd << endl; 
		n++;
		//让结果隔一秒显示一次
		Sleep(1000);
	}
}

4. ax + by = c 的情况

上面介绍的是 ax + by = gcd(a, b), 那么多余ax + by = c的情况要怎么处理呢?

  1. 如果c%gcd(a, b) 不等于0的话,方程无解
  2. 如果c%gcd(a, b) 等于0的话,t = c/gcd(a, b), 先求得ax + by = gcd(a, b) 的解,然后在乘以t即可。容易错误的是别感觉通解是x+n * (b/gcd) * (c/gcd),还是x+n*(b/gcd)

5.拓展欧几里德算法求逆元

逆元定义:逆元和我们平时所说的倒数是有一定的区别的,我们平时所说的倒数是指:a*(1/a) = 1,那么逆元和倒数之间的区别就是:假设x是a的逆元,那么 a * x = 1(mod m),也就是只多了一个取余的操作,这个取余的操作,就会保证a的逆元不一定只是a的倒数。
想要理解上面是什么意思,我们需要先知道 x = a mod b 是什么意思这个意思是x 与 a同余,比如x = 1mod 60, x可以取61等。其次就是 a * x = 1(mod m)等价于 ax + my = 1
逆元更通俗一点讲,就是求是的a*x%m = 1的最小整数解x。可以写为 ax + my = 1。利用拓展欧几里德进行求解。

代码如下:
由于gcd等于1,所以就可以去掉这个参数。

void exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
    }
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值