我看到许多人更喜欢在代码中使用:
while(i<1000000){
ret+=a[i];
i++;
if(ret >= MOD)
ret -= MOD;
}
而不是在最后一步中制作ret%MOD
。
这两者之间的区别是什么?这两者是否相等?
如何优化我们的代码?
答案 0 :(得分:2)
基本上你没有尝试就说不出来。有两种可能的结果(考虑下面我的注释):
编译器以某种方式优化代码,两种解决方案都使用条件跳转或模运算。这不仅取决于编译器的“亮度”,而且还必须考虑目标架构的可用指令集(但说实话,没有模运算会很奇怪。)
编译器不优化代码(最可能用于非优化调试版本)。
基本的区别 - 如前所述 - 使用if()
的解决方案将使用一个条件跳转 - 再次取决于您的架构 - 可能会减慢您的速度,因为编译器无法预取下一条指令没有先评估跳转条件。
进一步说明:
使用模运算或您的if()
语句实际上不等于(取决于实际值),这仅仅是因为ret % MOD
会导致以下相同的代码:
while (ret >= MOD)
ret -= MOD;
想象一下,a[i]
大于MOD
且新总和大于MOD
两倍。在这种情况下,你的ret
大于MOD
,这在使用模数时不会发生。
答案 1 :(得分:2)
举个例子:
13 MOD 10 它实际上做的是,在将13除以10之后给你提醒。
即:13 - (10 * (int)(13/10)) = 13 - ( 10 * 1 ) = 3
所以如果a[i] <= mod
那么它会很好用。但如果a[i] > mod
然后看到,会发生什么
let a[]= {15,15,15}
mod=7
in first step
ret = 0 + 15
ret = 15 - 7 = 8
2nd step
ret = 8 + 15 = 23
ret = 23 - 7 = 16
3rd step
ret = 16 + 15
ret = 31 - 7 = 24
所以你的最终结果是24,但它应该是3。
你必须这样做:
while (ret >= MOD)
ret -= MOD;
如果你想使用减法而不是mod ..
显然sub在时间方面优于mod ...因为mod非常耗时:(
答案 2 :(得分:1)
除非遇到性能问题,否则最好不要尝试优化代码。然后找出实际存在问题的地方
回答你的问题,两者是相同的 - 但你需要检查特定的硬件/编译器进行检查。
答案 3 :(得分:1)
模数需要整数除法,这通常是CPU上最慢的整数运算。很久以前,在管道和分支预测之前,这段代码可能比模数更快。如今分支机构可能非常缓慢,因此它的好处远非确定。如果a
中的值总是远小于MOD
,那么它可能仍然是一个胜利,因为分支将被跳过大多数迭代,并且分支预测器将主要猜测正确。如果它们不小,那就不确定了。您需要对两者进行基准测试。
如果您可以编写程序使MOD
始终为2的幂,则可以使用比任何一个快得多的位掩码。
如果我在1978年或2年代的代码中看到这种模式,并附有评论,解释作者如何对其进行基准测试,发现它比当前编译器,典型用户CPU和现实模块上的模数更快数据输入,我会翻白眼。
答案 4 :(得分:1)
条件测试和减法通常比模数运算便宜,特别是如果总和不经常超过MOD。模运算实际上是整数除法指令,其通常具有比比较/减法的等待时间大一个数量级的等待时间。话虽如此,除非循环是一个已知的性能瓶颈,否则你应该编写清晰度和健壮性的代码。
答案 5 :(得分:0)
是的摊位计算同样的事情,但是:
操作%
需要整数除法,这比[{1}}和-
if
变体通常更快(%
拖延管道)仍然存在if
变体更快的平台
编译器
现在您的案例与算法方面存在差异:
-=,if
while(i<1000000){ ret+=a[i]; i++; if(ret>=MOD) ret-=MOD; }
max(a[i])+MOD*N
取决于N
a[i]
将转到bignums,那么由于不需要增加子结果位宽,这将有更快的速度sum(a[i])
while(i<1000000){ ret+=a[i]; i++; } ret%=MOD;
无法保存非模结果ret
}
while(i<1000000){ ret+=a[i]; i++; ret%=MOD;
不是模运算
if (ret>=MOD) ret-=MOD;
while (ret>=MOD) ret-=MOD;
就可以了if
或模数以确保正确的结果