c++用数组int gcd辗转相除法_python(四):辗转相除

摘要:

学习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。这意味着结果可以表示为两个已知体积壶的整数线性组合:

equation?tex=z+%3D+mx%2Bny

所以,任意给定(x, y, z)且z<(x + y)后,那么就看上述方程是否有整数解(m, n),如果整数解存在的话结果就是True,否则就是False。

2、整数解存在的判据:裴蜀定理

前述方程是否有整数解的解法有一个已经存在的定理(裴蜀定理),其结论是该方程有整数解(m, n)的充要条件是z为x与y的最大公约数gcd(x, y)的倍数。也就是说,如果这个方程有整数解,那么z一定是

equation?tex=W%2Agcd%28x%2C+y%29%28W+%5Csubseteq+Z%29 ,反过来看,也只有当z%gcd(x,y)=0时,整数解才存在。于是,
这个问题转化为求x和y的最大公约数,有辗转相除法(欧几里得算法):

(2.1)核心思想,是如下等式,对于正整数p和q(若p>q),p除以q的余数为r,则有:

equation?tex=gcd%28p%2Cq%29%3Dgcd%28q%2Cr%29

由于显然有p>q>r,这个等式将两个较大数的公约数求解转化为两个较小数的公约数;

(2.2)这个最大公约数等式可以往下继续写:

equation?tex=gcd%28p%2Cq%29%3Dgcd%28q%2Cr_1%29%3Dgcd%28r_1%2Cr_2%29%3D...gcd%28c%2C0%29

其中从第一个等式开始,后式括号里的第一项为前式括号里的较小数、第二项为前式括号里较大数和较小数相除的余数。最后总能到一个余数为0的情况,此时的第一项即为p和q的最大公约数;

(2.3)这个算法可以用递归的思想很好地表达出来,python代码如下:

d802c82224b09b23d7baad6c9d6dbb09.png

test of gcd:

4b76eba7624e153d3a728dccbdfbcb76.png
这两组数的最大公约数都是6

那么,leetcode_365就迎刃而解了,代码如下:

dc0dc850c7d035445068a7d220a237e4.png

test of canMeasureWater:

c365e1b8f4fd718cdd00d48369462da0.png
题目的example

2e8e600a05cc33252b3c9cbe2e2ec6cc.png
随机输入3个正整数进行测试

3、整数解的求解:扩展欧几里得算法

判断了能否测量,进一步就会思考如何进行测量呢?这个实际上就是在求前述方程的整数解。这里的解法类似于辗转相除法,也即扩展欧几里得算法。需要注意的是,由于px+qy=c的整数解从几何意义上来说是检查一条直线过整数格点的情况,所以当然可以不止一个解,而扩展欧几里得算法得到的只是其中一个。

首先从一个例子来感受一下该算法:

equation?tex=20x_0%2B12y_0%3D4

(a)方程系数(20, 12)变为(12, 8):

equation?tex=12x_0%2B8x_0%2B12y_0%3D4+%5CRightarrow+12%28x_0%2By_0%29%2B8x_0+%3D4

假设

equation?tex=x_1%3Dx_0%2By_0%EF%BC%8Cy_1%3Dx_0%28x_0%3Dy_1%2C+y_0%3Dx_1-y_1%29 ,则变为:

equation?tex=12x_1%2B8y_1%3D4

(b)继续将(12, 8)变为(8, 4):

equation?tex=8x_1%2B4x_1%2B8y_1%3D4+%5CRightarrow+8%28x_1%2By_1%29%2B4x_1+%3D4

假设

equation?tex=x_2%3Dx_1%2By_1%EF%BC%8Cy_2%3Dx_1%28x_1%3Dy_2%2C+y_1%3Dx_2-y_2%29 ,则变为:

equation?tex=8x_2%2B4y_2%3D4

(c)继续将(8, 4)变为(2, 0):

equation?tex=8x_2%2B4y_2%3D4+%5CRightarrow+4%282x_2%2By_2%29%3D4

假设

equation?tex=x_3%3D2x_2%2By_2%EF%BC%8Cy_3%3Dx_2%3D0%28x_2%3Dy_3%3D0%2C+y_2%3Dx_3-2y_3%3Dx_3%29 ,则变为:

equation?tex=4x_3%3D4

(d)最后一个解(x3, y3)=(1, 0),那么从括号里的递推公式,依次有:(x2, y2)=(0, 1),(x1, y1)=(1, -1),(x0, y0)=(-1, 2),最后一项即为原方程的一个解。

形如

equation?tex=px%2Bqy%3Dc 的方程总是可以用(a)-(d)来求解。整个过程和辗转相除类似(方程系数就是在做辗转相除),而且如果方程有解(c%gcd(p,q)=0),那最后的解肯定是(c/gcd(p,q), 0),因为系数辗转相除总是会到(gcd(p,q), 0)。其中变量转换的递推公式比较关键,但也很好理解,推广一下就是(//和python的整数除法定义一致):

equation?tex=x_n%3Dy_%7Bn%2B1%7D%2C+y_n%3Dx_%7Bn%2B1%7D-%28p_n%2F%2Fq_n%29y_%7Bn%2B1%7D

python实现代码如下:

16a4914f8fe1cb3c5c57d45e96d5f858.png
这里先求解px+qy=gcd(p,q),再转换为px+qy=c的解;这样操作只是为了逻辑更清晰,实际上可以整合在一起(递归结束时返回值从(1, 0)变为(c/gcd(p,q), 0))

2e7aa7a452cde1a1668f40a897bb6802.png
px+qy=gcd(p,q)的解,求解过程不依赖于方程左边的c;以(p,q)为递归过程,再反向递归求解(x,y)

test of integer_solution:

bbec8c680027d9cfaea5287ca53bb10c.png
实际上还有x=-2, y=2的解,多解的原因应该是前述(c)步骤中,最后一次变量转换实际上可以不遵从递推公式

从这个结果可知,如果想用3升和5升的水壶生成4升容积,解(8,-4)意味着总共会倒入8次3升水壶、倒掉4次5升水壶,而解(-2, 2)意味着总共会倒掉2次3升水壶、倒满2次5升水壶。当然,解只是总体概括,至于具体步骤还需要进一步地分解了,不过问题就已经变得很简单了。

4、本篇内容代码的github链接

gcd_euclidean_algorithm.py​github.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值