贝祖等式
- 对任何整数**a、b**和它们的**最大公约数d**,关于未知数x和y的线性丢番图方程(称为裴蜀等式):
**ax + by = m 有整数解时当且仅当m是d的倍数。**
裴蜀等式有解时必然有无穷多个整数解,每组解x、y都 称为裴蜀数,可用扩展欧几里得算法(Extended Euclidean algorithm)求得。
- 方程12x+42y=6有解
- 特别地,方程 ax+by=1 有整数解当且仅当整数a和b互素
- 扩展欧几里得算法就是在求a,b 的最大公约数 m = gcd(a,b) 的同时,求出贝祖等式ax + by = m的一个解 (x0,y0)
- 如何递推?
- x = y1
- y = x1 - a/b*y1
- 通解:
- x = x0 + (b/gcd) * t 所有的x对b同模
- y = y0 - (a/gcd) * t 所有的y对a同模
- 如果想要得到x大于0的第一个解?
- b/=d; x = (x0%b+b) %b
- 蓝桥杯2016决赛:一步之遥
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。
矿车上的动力已经不太足,黄色的警示灯在默默闪烁...
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。
注意,需要提交的是一个整数,不要填写任何无关内容(比如:解释说明等)
其实就是求线性方程罢了。
思路: 运用到递推的方式求出答案,运用到了静态内部类的方式。
具体思路:
- 创建一个私有的静态内部类,比较好做分解x,y
ax+by=m 当m时gcd(a,b)倍数时有解
其实等价于ax = m mod b
- 先执行
long d = ext_gcd(a, b);
调用函数 - 开始先设定好边界
if (b == 0)
- 例如线性方程:2x + 7y = 1求解整数解
a = 2, b = 7, m = 1
(2,7)
(7,2)
(2,1)
(1,0)
在递推公约数的时候,更新x和y
(2,7) x1 = -3, y1 = 1
(7,2) x1 = 1, y1 = -3
(2,1) x1 = 0, y1 = 1
(1,0) x1 = 1, y1 = 0 - 最后再判断是否有解等价于
ax = m mod b
public class Case06_一步之遥 {
public static void main(String[] args) {
try {
long ans = ExtGcd.linearEquation(97, -127, 1);
long x = ExtGcd.x;
long y = ExtGcd.y;
System.out.println(abs(x) + abs(y));
} catch (Exception e) {
e.printStackTrace();
}
}
//私有的静态的内部类
private static class ExtGcd {
static long x, y;
/**
* 扩展欧几里得
* 调用完成后xy是ax+by=gcd(a,b)的解*/
public static long ext_gcd(long a, long b) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long res = ext_gcd(b, a % b);
//x,y已经被下一层递归更新了,ppt中所说的x0和y0
long x1 = x;//备份x
x = y;//更新x
y = x1 - a / b * y;//更新y
return res;
}
/**
* 线性方程
* ax+by=m 当m时gcd(a,b)倍数时有解
* 等价于ax = m mod b*/
public static long linearEquation(long a, long b, long m) throws Exception {
long d = ext_gcd(a, b);
//m不是gcd(a,b)的倍数,这个方程无解
if (m % d != 0) throw new Exception("无解");
long n = m / d;//约一下,考虑m是d的倍数
x *= n;
y *= n;
return d;
}
}
}