扩展欧几里得算法
请先熟练理解欧几里得算法定义 以及下面的欧几里得算法代码板子
任何代码,思想最重要!
思想:
先回忆欧几里得算法
辗转相除法求最大公约数
具体的辗转相除法定义,请百度…
//enen 真的简洁 真的妙不可言
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
扩展欧几里得算法思想:
gcd(a,b) 在下面中均 指a,b 的最大公约数
首先 要明白的是 对于任意正整数 a,b 一定存在一对非0 整数 x ,y
使得 ax + by = gcd(a, b)
意思是在欧几里得算法里新添加了两个量 x,y 即每层递归中都存在一对 x,y使得 ax + by = gcd(a, b)
成立
这里的a,b对应的是每层递归里的a,b(欧几里得算法)
最终要求 求得最开始的 使得ax + by = gcd(a, b)
成立的一对 x,y的值
这问题看起来 很让人蒙蔽 enen是人脑难以解决的问题
其实我们很容易求出递归到最后一层的x,y
也就是 最后一组ax + by = gcd(a, b)
的x,y值 很明显 此时x=1,y=0
why?
reason:
原因 (欧几里得算法中) 递归到最后时a = 最大公约数 ,b = a%b(此层递归上面的b) 。所以若想此时的公式ax + by = gcd(a,b)成立 ,只有 x = 1,y=0 这时我们就可得知递归到 最后一层的 x,y 了。
如果我们想求出最开始的x,y最好的想法就是找出相邻层的每一对x,y之间的关系 根据递归的回溯 从而求出最开始的 x ,y的值。
问题转化为-----
求相邻层之间的 每一对的 x,y的关系:
这里先看第一层的 x,y的值:
a * x1 + b * y1 = gcd(a,b);
第二层的x,y的值:
b * x2 + a % b * y2 = gcd(a,b);
又因为 a % b = a - (a / b) * b
<-----小学知识
所以第二层的x,y的值:
b * x2 + (a - (a / b) * b) * y2 = gcd(a,b);
化简第二层的得:
b * x2 + a * y2 - (a / b ) * b * y2 = gcd(a,b);
b * (x2 - (a / b) * y2) + a * y2 = gcd(a,b);
对应一二层a,b的系数 可求得相邻层之间x,y的关系
y1 = x2 - (a / b) * y2
x1 = y2
因为递归是先知道最后面的 然后一层一层的向上推 知道上一层的值
很显然这里的 x1,y1是上一层 ,x2,y2是下一层。
因此根据相邻层间每对的x,y的关系,就可以一层层的从已知的最后一层,推出最开始的x,y的值。
思想毕,上代码:
普通代码:
int exgcd(int a, int b, int& x, int& y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int temp = y;
y = x - (a / b) * y;
x = temp;
return d;
}
升级版:
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
这个 x,y对调位置在回溯的时候省略了 temp 秒啊!!