学习拓展欧几里得算法的目的是为了解决直线上的点的问题:求直线ax + by +c = 0 上有多少个整点(x, y)满足x∈[x1, x2], y∈[y1, y2];(在此为了推导方便,把ax +by + c = 0换成 ax +by = c, 效果相同)
在下面我会用@1解决@2,再用@2解决@3,最后回来解决@1, 这样就解决了@3(直线上的点的问题)
拓展欧几里得算法: ax + by = gcd ( gcd 表示gcd(a, b), 下同) @1
假设c/gcd为整数, 上式两边*(c/gcd) 得ax(c/gcd) + by(c/gcd) = c @2 (若c/gcd不为整数解,则直线上的点的问题也不会有整数解)
由上式解可得ax + by = c 的一组解 ,即可得直线上的点的问题的一组解。 由这组解(x。, y。)去求全部解:(x。+s。,y。- t。); @3
a(x。+s。) + b(y。- t。) = c ,结合ax。+by。= c可得:as。= bt。两边 / gcd ,得:a / gcd *s。= b / gcd * t。由于a/gcd与b/gcd互质,则s。必为b/gcd的倍数,
设为s。= k*b/gcd , 可推出t。= k*a/gcd
则ax + by =c 的解为x = x。+k*b/gcd, y = y。- k *a/ gcd; k 为任意整数
x 的最小非负数解为((x。% (b/gcd) ) + b/gcd)%(b/gcd) 写的这么麻烦是防止x。为负数时出错
注意(x。, y。)是ax+by = c的一组解,直线上点的坐标要用拓展欧几里得里的解表示则为:x = x 1c/gcd+ kb/gcd; y = y 1c/gcd - ka/gcd (x1, y1)为拓展欧几里得里的一组解
回来解决@1:
我们先解决这个问题: 若ax 1+ by1=gcd, bx2 + (a%b)y2 = gcd ,如何通过(x2, y2)求得(x1, y1)的一组解
首先 :a%b = a- a/b*b (计算机的思维就是这么妙)
联立上式得:ax1+by1=ay2 + b(x2-(a/b)y2) ,可以得到它的一组解(注意就是一组解) x1 = y2; y1 =x2 - (a/b)y2 @4
解决这个问题后,我们就来解决ax+by =gcd的问题
首先gcd(a, b) = gcd ( b, a%b) ,边界:b = 0
则把上式不断变换,得到 a * x + 0 * y= gcd(a, 0) = a, 注意:这里的a,x,y都不是原来的a, x, y了, 在这里可取 x =1 , y =0 ; a = gcd (a, 0) = gcd(gcd为原来的a,b 的最大公因数),可得a值
然后通过@4的式子不断回溯,可得最初的ax + by = gcd 的一组解
最后和@3中差不多 ,设全部解:(x1+s。,y1 - t。),然后代入,解得x = x1+k*b/gcd, y = y1- k *a/ gcd;
x 的最小非负数解为((x1% (b/gcd) ) + b/gcd)%(b/gcd)
拓展欧几里得算法:
1 int exGcd(int a, int b, int &x, int &y) 2 { 3 if(b == 0) 4 { 5 x = 1; 6 y = 0; 7 return a; 8 } 9 int g = exGcd(b, a % b, x, y); //g 是 gcd 10 int temp = x; 11 x = y; 12 y = temp - a / b * y; 13 return g; 14 }
嗯,基本就是这些了
补充:
1.思考了a, b为负数的情况,当a,b 为负数时代码应该要有调整,使得传入exGcd的a, b为正数,并对对应的x或y进行取反操作;