欧几里德算法如何工作?

时间:2011-05-14 23:59:31

标签: java algorithm greatest-common-divisor

我刚刚发现这个算法来计算我的讲义中最大的公约数:

public static int gcd( int a, int b ) {
    while (b != 0) {
        final int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

所以 r 是将 b 分成 a (得到mod)时的余数。然后将 b 分配给 a ,并将余数分配给 b ,并返回 a 。我不能为我的生活看到它是如何运作的!

然后,显然这个算法并不适用于所有情况,然后必须使用这个算法:

public static int gcd( int a, int b ) {
    final int gcd;
    if (b != 0) {
        final int q = a / b;
        final int r = a % b; // a == r + q * b AND r == a - q * b.
        gcd = gcd( b, r );
    } else {
        gcd = a;
    }
    return gcd;
}

我不明白这背后的原因。我通常得到递归并且擅长Java,但这是在逃避我。请帮忙吗?

5 个答案:

答案 0 :(得分:25)

Wikipedia文章包含一个解释,但要立即找到它并不容易(同时,程序+证明并不总是回答“为什么会这样做”)。

基本上,它归结为这样的事实:对于两个整数a,b(假设a> = b),总是可以写a = bq + r,其中r <1。湾

如果d = gcd(a,b),那么我们可以写a = ds和b = dt。所以我们有ds = qdt + r。由于左侧可被d整除,因此右侧也必须可被d整除。由于qdt可被d整除,因此结论是r也必须能被d整除。

总结:我们有一个= bq + r,其中r < b和a,b和r都可被gcd(a,b)整除。

因为&gt; = b&gt; r,我们有两个案例:

  1. 如果r = 0则a = bq,因此b除以b和a。因此gcd(a,b)= b。
  2. 否则(r> 0),我们可以减少找到gcd(a,b)的问题,找到gcd(b,r)这个数字完全相同的问题(a,b和r都是可以被d)整除。
  3. 为什么减少?因为r <湾所以我们正在处理的数字肯定更小。这意味着我们只需要在达到r = 0之前将这种减少应用有限次数。

    现在,r = a%b,希望能解释你的代码。

答案 1 :(得分:1)

他们是等同的。首先要注意的是,第二个程序中的q根本没有使用。另一个区别是迭代与递归。

至于它的工作原理,上面链接的维基百科页面很好。第一个插图特别有效地直观地传达了“为什么”,然后下面的动画说明了“如何”。

答案 2 :(得分:0)

鉴于从未使用'q',我没有看到你的普通迭代函数和递归迭代函数之间存在差异...两者都

gdc(first number, second number)
  as long as (second number > 0) {
      int remainder = first % second;
      gcd = try(second as first, remainder as second);
  }
}

除非尝试将此应用于非整数,否则此算法会失败?

(有关详细信息,请参阅http://en.wikipedia.org/wiki/Euclidean_algorithm

答案 3 :(得分:0)

这是一篇有趣的博文:Tominology

在讨论欧几里德算法背后的许多直觉时,它是用JavaScript实现的,但我相信如果想要的话,将代码转换为Java并不困难。

答案 4 :(得分:-1)

Here是我发现的一个非常有用的解释。

对于那些懒得打开它的人来说,这就是它所说的:

当你必须找到(3084,1424)的GCD时,请考虑这个例子。让我们假设d是GCD。这意味着d | 3084和d | 1424(使用符号&#39; |&#39;说&#39;划分&#39;)。

遵循d | (3084 - 1424)。现在我们尝试尽可能地减少这些可被d整除的数字(在这种情况下为3084和1024),以便我们将0作为数字之一。请记住,GCD(a,0)是一个。

自d | (3084 - 1424),它遵循d | (3084 - 2(1424)) 这意味着d | 236.
提示:(3084 - 2 * 1424 = 236)

现在忘记了初始数字,我们只需要解决d,我们知道d是分为236,1424和3084的最大数字。所以我们使用较小的两个数字来继续,因为它&#39; ll将问题收敛于0。

d | 1424和d | 236意味着d | (1424 - 236)。
所以,d | (1424 - 6(236))=&gt; d | 8.

现在我们知道d是划分8,236,1424和3084的最大数字。再次采用较小的数字,我们有

d | 236和d | 8,暗示d | (236 - 8)。
所以,d | (236 - 29(8))=&gt; d | 4.

再次,被d整除的数字列表会增加并收敛(数字越来越小,越接近0)。就目前而言,d是划分4,8,236,1424,3084的最大数字。

采取相同步骤,

d | 8和d | 4意味着d | (8-4)。
所以,d | (8 - 2(4))=&gt; d | 0.

可被d整除的数字列表现在为0,4,8,236,1484,3084。 (a,0)的GCD始终是a。所以,一旦你将0作为两个数字中的一个,另一个数字是原始的两个gcd和所有介于两者之间的gcd。

这正是您的代码正在做的事情。您可以将终端条件识别为GCD(a,0)= a。

另一步是找到两个数字的其余部分,然后选择前两个中的较小数字作为新数字。