我创建了一个方法,允许我找到两个数字的GCF / GCD,虽然我有一个工作代码,但我不知道它是如何工作的或原因。我理解Euclid的算法,但不确定以下代码片段如何使用它。
private int gcd(int a, int b)
{
if (b == 0)
return a;
else if(a ==0)
return b;
else
return gcd(b, a % b);
}
我对它返回的内容特别困惑,因为为什么要返回两个值?什么是a%b做什么?这如何使用Euclid的算法?
答案 0 :(得分:0)
1)a%b做什么?
%是Java中的模数或余数运算符。 %运算符返回两个数字的余数。例如,8%3是2,因为8除以3留下余数2。
2)欧几里德算法基于这样的原理:如果较大的数字被较小的数字替换为较大的数字,则两个数字的最大公约数不会改变。实际上,你的gcd函数被用作欧几里德算法的更高效版本。这个版本将两个数字中较大的一个替换为余数除以两者中较小的一个(使用此版本,算法在达到零余数时停止)。加布里埃尔·拉姆于1844年(https://en.wikipedia.org/wiki/Euclidean_algorithm)
证实了这一点 3)你的gcd函数没有返回两个值,它是一个递归函数。递归函数是一个函数,它既可以调用自身,也可以处于函数调用的潜在循环中。在你的gcd功能的情况下,它将重复,直到两个参数之一变为零并且gcd值是保持参数。
您可以在此链接中了解有关递归函数的更多信息
http://pages.cs.wisc.edu/~calvin/cs110/RECURSION.html
答案 1 :(得分:0)
"如果是,则两个数字的最大公约数不会改变 较大的数字被其与较小数字的差异所取代。"
(wikipedia - Euclidean algorithm)
所以,模数:
Modulo返回两个整数之间的整数除法的余数。整数除法是没有分数或浮点的除法。我们将整数除法表示为m /\ n
。
m /\ n = o;
m % n = p;
o * n + p = m;
举个例子,
29 /\ 3 = 9; (3 goes - whole - into 29 9 times)
29 % 3 = 2; (the integer division of 29 into 3 has a remainder of 2)
9 * 3 + 2 = 29; (9 goes into 29 3 times (and 3 goes into 29 9 times), with a remainder of 2)
请注意,如果m
小于n
(即m < n
),那么n
会进入m
0次(m /\ n = 0
) ,因此整数除法的余数将为m
(m % n = m
,因为o * n + p = m
因此(0*n) + p = 0 + p = p = m
);
那么,该功能如何运作?让我们尝试使用它。
1 - gcd(m,n),m&lt; Ñ强>
因此,如果我们使用小于gcd(m, n)
的{{1}}开始m
,则在下一次嵌套调用gcd时发生的唯一事情就是参数的顺序发生变化:{ {1}};
2 - gcd(n,m),m&lt; Ñ强>
好的,现在第一个参数大于第二个参数
根据欧几里得的算法,我们希望对两个数字中较大的一个做一些事情。我们想用它和较小的数字之间的差异来替换它。我们可以做n
多次。但gcd(n, m % n) = gcd(n, m)
所做的事情与从m - n
中尽可能多地从m % n
中减去n
完全相同之前这样做会导致负数。减法看起来像m
,依此类推。但如果我们扩展它,我们得到:
(((m - n) - n) - n) - n)
。由于m - (n * o)
,我们可以看到o * n + p = m
和m - (n * o) = p
。因此,重复从较大值中减去较小的值与使用较小值的较大值的模数相同
在下一步中,第二个参数可以是0(如果p = m % n
是n
的除数)。在这种情况下,函数返回n。这是正确的,因为n是它自身的除数,而且正如我们所见,它是m
的除数。
或者,第二个参数可能小于m
。这是因为n
到m
的整数除法的余数必须小于n
- 这是因为,如果除法的余数大于n
,那么n
可能会再次进入n
,但事实并非如此;这是一个荒谬的结果。假设m
不是0,那么第二个参数(让我们称之为n
)小于p
。
所以,我们现在调用n
,其中gcd(n, p)
。
3 - gcd(n,p),p < Ñ强>
现在发生了什么?好吧,我们和前一段完全一样。现在我们只重复那一步,即我们将继续调用p < n
,直到传递给gcd(a, b)
的两个数中较小的一个是两个数中较大者的除数,(意味着{ {1}})在这种情况下,您只需返回两个数字中较小的一个。
答案 2 :(得分:0)
鉴于你的问题有一些组成部分,我将讨论欧几里得经典算法在你提出的递归方法中的演变。请注意,此处介绍的方法假设a >= b
下面的方法最有可能实现您熟悉的算法,该算法会从b
(较大的数字)重复减去a
(较小的数字),直到它不再大于或等于到b
。如果a == 0
,则没有余数,将b
作为GCD。否则,交换a
和b
的值并继续重复减法。
public int classic_gcd(int a, int b) {
while (true) {
while (a >= b)
a = a - b;
if (a == 0)
return b;
int c = b;
b = a;
a = c;
}
}
由于内部while循环,基本上计算a
除以b
的提醒,它可以用模数运算符替换。这极大地提高了算法的收敛速度,用单模运算代替了潜在的大量减法。考虑找到12,288和6的GCD,这将导致超过2,000减法。这种改进在下面的修改方法中显示。
public int mod_gcd(int a, int b) {
while (true) {
int c = a % b;
if (c == 0)
return b;
a = b;
b = c;
}
}
最后,修改后的算法可以表示为递归算法,即它自己调用,如下所示:
public int recurse_gcd(int a, int b) {
if (b == 0)
return a;
else
return recurse_gcd(b, a % b);
}
此方法与以前完成相同。但是,该方法不是循环,而是调用自身(如果没有检查则是无限循环)。通过更改传递给方法的参数的顺序来完成值的交换。
请注意,上述方法纯粹用于演示,不应在生产代码中使用。