今天做面试题,又遇到了求两个数的最大公约数(gdc)问题,题虽简单,但还是通过比划了一两个例子,才想起了辗转相处的思路来。
(1) r = a%b
(2)r != 0, a = b,b = r 转(1)
(3)r = 0 ,return b
几行代码就能搞定,但记住这些步骤,就如同记住了某个库函数。遇到相同的问题我们能很快处理,此时知识与问题之间是一对一的关系。因不了解其原理,大脑里总有一个问号,why it does work?。如果原理性的东西不高明白,那么我们就无法做到举一反三。就如同一个强大的武器仅发挥了10%的作用,还90%被我们给浪费了。就如同库函数,不了解其源码,处理错就不知该如何下手,我们就无法回答向 它在什么时候使用,能处理什么问题,在什么情况下效率最高这类问题。
于是开始寻找辗转相处法的证明:
该方法将 gdc(a,b),转化为gdc(b,r),其中r=a%b,因此我们要证明的就是gdc(a,b)=gdc(b,r)
证明:令c = gdc(a,b),k = a/b,r = a%b;设 a = mc, b = nc;
=>r = mc - knc
=>r = (m - kn)c=>c 为 r的因子 (1)
接着证明c为b,r的最大公因子,即 m-kn 与 n 互质,用反证法
假设 m-kn 与 n 存在公因子 d,则 r = xdc, b = ydc => a = kydc + xdc = (ky+x)dc
=> gdc(a,b) = dc 与题设矛盾故 m-kn 与 n 互质。(2)
综合(1)、(2)可知 c 为b ,r的最大公约数
证毕。
现在脑海中的困惑没了,这个feel,很爽...
越来越明显的感到,曾经费很大精力搞明白的东西,过一段时间再看又想不起来了,我也一直再寻觅一种跳出这种窘境的办法,好的办法还没发现,但有些基本规律是不变的。1、得之越难,印象越深。 2、解题的方法,比具体的步骤更重要。 3、问题的本质,比现象更重要。不仅要通过试错找到正确的路径,更要明白为什么这条路正确。否则,稍有变化,又要不断的试错,很痛苦,很耗时。
#include <iostream>
using namespace std;
/*
** 求两个数的最大公约数
** in: a
** <span style="white-space:pre"> </span>in: b
** out: gdc 最大公约数 -1 error
*/
int gdc(int a, int b)
{
if(a == 0 || b==0 ){
cout << "error inupt 0\n";
return -1;
}
int out,r,temp;
out = a > b ? a : b;
r = a > b ? b : a;
while (r)
{
temp = r;
r = out%r;
out = temp;
}
return out;
}
辗转相除法不必关心a,b谁更大,因此上面代码中判断a,b大小的部分可以省略,《编程之美》给出了十分优雅的代码
int gcd(int a, int b)
{
return (!b) ? a : gcd(b,a%b);
}