摘要:
学习leetcode_365: Water and Jug Problem的解法:辗转相除相关内容(最大公约数、裴蜀定理、欧几里得算法和扩展欧几里得算法)。
正文:
1、问题描述
You are given two jugs with capacities x and y litres. There is an infinite amount of water supply available. You need to determine whether it is possible to measure exactly z litres using these two jugs.
If z liters of water is measurable, you must have z liters of water contained within one or both buckets by the end.
Operations allowed:
- Fill any of the jugs completely with water.
- Empty any of the jugs.
- Pour water from one jug into another till the other jug is completely full or the first jug itself is empty.
Example 1:
Input: x = 3, y = 5, z = 4
Output: True
(**分析**):
首先,将三个操作进行“量化”,一次倒满已知体积的壶的操作,相当于有一个(+1)*Volume,而一次将满壶水倒掉的操作,则相当于(-1)*Volume。这意味着结果可以表示为两个已知体积壶的整数线性组合:
所以,任意给定(x, y, z)且z<(x + y)后,那么就看上述方程是否有整数解(m, n),如果整数解存在的话结果就是True,否则就是False。
2、整数解存在的判据:裴蜀定理
前述方程是否有整数解的解法有一个已经存在的定理(裴蜀定理),其结论是该方程有整数解(m, n)的充要条件是z为x与y的最大公约数gcd(x, y)的倍数。也就是说,如果这个方程有整数解,那么z一定是
(2.1)核心思想,是如下等式,对于正整数p和q(若p>q),p除以q的余数为r,则有:
由于显然有p>q>r,这个等式将两个较大数的公约数求解转化为两个较小数的公约数;
(2.2)这个最大公约数等式可以往下继续写:
其中从第一个等式开始,后式括号里的第一项为前式括号里的较小数、第二项为前式括号里较大数和较小数相除的余数。最后总能到一个余数为0的情况,此时的第一项即为p和q的最大公约数;
(2.3)这个算法可以用递归的思想很好地表达出来,python代码如下:
test of gcd:
那么,leetcode_365就迎刃而解了,代码如下:
test of canMeasureWater:
3、整数解的求解:扩展欧几里得算法
判断了能否测量,进一步就会思考如何进行测量呢?这个实际上就是在求前述方程的整数解。这里的解法类似于辗转相除法,也即扩展欧几里得算法。需要注意的是,由于px+qy=c的整数解从几何意义上来说是检查一条直线过整数格点的情况,所以当然可以不止一个解,而扩展欧几里得算法得到的只是其中一个。
首先从一个例子来感受一下该算法:
(a)方程系数(20, 12)变为(12, 8):
假设
(b)继续将(12, 8)变为(8, 4):
假设
(c)继续将(8, 4)变为(2, 0):
假设
(d)最后一个解(x3, y3)=(1, 0),那么从括号里的递推公式,依次有:(x2, y2)=(0, 1),(x1, y1)=(1, -1),(x0, y0)=(-1, 2),最后一项即为原方程的一个解。
形如
python实现代码如下:
test of integer_solution:
从这个结果可知,如果想用3升和5升的水壶生成4升容积,解(8,-4)意味着总共会倒入8次3升水壶、倒掉4次5升水壶,而解(-2, 2)意味着总共会倒掉2次3升水壶、倒满2次5升水壶。当然,解只是总体概括,至于具体步骤还需要进一步地分解了,不过问题就已经变得很简单了。
4、本篇内容代码的github链接
gcd_euclidean_algorithm.pygithub.com