我正试图解决欧拉问题:目前,我的速度定义步骤是计算两个数字的最小公倍数。那么,这些方法中哪一种更快呢?为什么?
public static int lcm(int a, int b){
for(int test = a; true; test += a){
if(test % b == 0)
return test;
}
}
或
public static int lcm(int a, int b){
for(int i = 1; true; i++){
if(i*a % b == 0)
return i*a;
}
}
我认为这里的根本问题是,乘法和加法哪个更快。
谢谢。
(在让我展示我的其余代码之前/说我不应该专注于程序的这一部分:我的问题不是如何得到问题的答案,而是如何使这一部分更快。)
粗略地说,为了创建快速代码,避免一切会导致汇编代码缓慢的事情。这包括:
-
临时(此处:循环变量)
-
常数(此处:true)
-
条件检查(此处:true)
-
乘法(此处:i*a)
-
Modulo(此处:测试%b)
-
不要依赖编译器优化
临时、模和一的比较是算法固有的,因此是不可避免的。所以它会产生这样的结果:
public int euler(int a, int b) {
int test = a;
while (test % b != 0) {
test += a;
}
return test;
}
如果你稍微修改(数字)算法为等效方程,你可以完全消除乘法:
public int own(int a, int b) {
int x = a;
for (int y = 0;; x += a) {
while (y < x) {
y += b;
}
if (x == y)
break;
}
return x;
}
顺便说一句:如果你要找到大数字的LCM,也许你最好使用欧几里得的GCD算法。因此LCM(a,b)=a*b/GCD(a,b)。Java类BigInteger.gcd()中已经有了一个有效的实现。
现在评论或预测哪一个更快还为时过早。
您的两个程序将进行相同数量的迭代,如下所述。
在情况1中,您将每一步增加一个
在情况2中,您将倍数增加1,这与增加a相同。
让我们以案例1 为例
public static int lcm(int a, int b){
for(int test = a; true; test += a){
if(test % b == 0)
return test;
}
}
在if
语句和return
语句中都必须避免乘法运算。
在情况2中,使用increment operator
并在if
和return
中相乘。它在每个if条件下相乘。
增量操作可以通过使用组件的增量操作来优化,因此它可以比添加更快
情况1中的额外时间是由增加的
在情况2中,情况2中的额外时间是递增的,并且在每个if条件下和在成功if条件的返回时相乘。
如果乘法比加法慢,则情况2比情况1稍慢。您可能只会在大量的测试运行中注意到细微的差异。
请注意,还有其他因素也会影响性能。
因此,在得出最终结论之前,必须对这两个代码进行分析,以了解所用时间的差异。