程序中的模数优化

时间:2014-12-14 08:58:33

标签: algorithm optimization modulo

我看到许多人更喜欢在代码中使用:

 while(i<1000000){
       ret+=a[i];
       i++;
       if(ret >= MOD) 
          ret -= MOD;
   }

而不是在最后一步中制作ret%MOD

这两者之间的区别是什么?这两者是否相等?

如何优化我们的代码?

6 个答案:

答案 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}}和-

更耗时
  • 但在现代并行机器上(意味着更多的管道不是核心)
  • CPU会立即执行更多任务,除非它们相互依赖或发生了运行
  • 这就是为什么在现代机器上if变体通常更快(%拖延管道)

仍然存在if变体更快的平台

  • 就像MCU一样,当你知道你只有一个CPU / MCU管道时
  • 或者分裂非常慢,然后使用此变体
  • 您应始终在优化过程中衡量结果关系
  • 在你的情况下,你想要每个循环调用一个mod,所以它应该更快,但检查后面的文本......

编译器

  • 现代编译器为您的目标平台优化代码,并且通常会检测到并使用正确的选择
  • 因此您不应该通过低级优化而不是通过编程任务功能来使用
  • 但并非所有编译器都适用于许多平台仍然使用较旧的编译器
  • 在极少数情况下,优先关闭优化
  • 因为它可能会破坏特定的期望时间,指令模式,甚至任务的功能......
  • 在这种情况下没有选择,这种知识突然变得方便
  • (这里的很多人都没有得到这个,并且急于向低级优化问题投票)

现在您的案例与算法方面存在差异:

  1. -=,if
    • 子结果仍然是modulo mod
    • 这意味着您不需要更多位,然后用于while(i<1000000){ ret+=a[i]; i++; if(ret>=MOD) ret-=MOD; } max(a[i])+MOD*N取决于N
    • 如果a[i]将转到bignums,那么由于不需要增加子结果位宽,这将有更快的速度
  2. sum(a[i])
    • 如果变量while(i<1000000){ ret+=a[i]; i++; } ret%=MOD;无法保存非模结果
    • ,则可能会溢出
  3. ret}
    • 这是更大的非模数结果应该如何
  4. while(i<1000000){ ret+=a[i]; i++; ret%=MOD;不是模运算
    • 它只是它的迭代。
    • 更安全的是if (ret>=MOD) ret-=MOD;
    • 但是如果你知道子结果没有增加太多(所以它不会在任何几次迭代中溢出)那么while (ret>=MOD) ret-=MOD;就可以了
    • 但在这种情况下,您应该在循环后添加if或模数以确保正确的结果