高效的Modulo 3操作

Efficient Modulo 3 operation?

本文关键字:操作 Modulo 高效      更新时间:2023-10-16

可能重复:
快速模3还是除法算法?

每个人都知道模运算可能是性能上的一个巨大缺点。有人知道x%3操作的好替代方案吗?我知道x%2存在一个缓冲区,但我真的需要一个用于模3的缓冲区,因为我想在for循环中的三个缓冲区之间交替。

谢谢!

好吧,不是通常的"测量它",而是一个实际的答案——因为这些东西实际上是真正有趣的数学。尽管编译器也可以而且可能做到这一点(至少在现代优化c++编译器中,javac肯定不会,我也不知道JVM是否做到了(,但最好检查一下它是否还没有为您完成这项工作。

但了解优化背后的理论仍然很有趣:我将使用汇编,因为我们需要乘法中更高的32位字。以下是沃伦关于比特游戏的书:

n是我们想要取模的输入整数:

li M, 0x55555556   ; load magical number (2^32 + 2) / 3
mulhs q, M, n      ; q = higher word of M * n; i.e. q = floor(M*n / 2^32)
shri t, n, 31      ; add 1 to q if it is negative
add q, q, t

这里q包含n/3的除数,所以我们照常计算余数:r = n - q*3

数学是有趣的部分——乳胶在这里会很酷:

q=楼层((2^32+2(/3*(n/2^32((=楼层(n/3+2*n/(3*2^32(

现在,对于n=2^31-1(有符号32位整数可能的最大n(,误差项小于1/3(并且是非负的(,这使得很容易证明结果确实是正确的。对于n=-2^31,我们有上面1的修正,如果你简化它,你会发现误差项总是大于-1/3,这意味着它也适用于负数。

我把误差项边界的证明留给感兴趣的人——这并不难。

如果是在一个直循环中,则无需计算模。保持第二个int var,每3步重置一次。

int i, bn = 0;
for(i=0; i<whatever; i++) {
  ...
  if(++bn == 3) bn = 0;
}

这不是一个过早的优化,它避免了不必要的计算。

编辑:OP中说他使用循环在缓冲区之间切换,所以我的解决方案看起来很合适。至于否决票,如果这是一个错误,那没问题。

如果在编译时已知3,则编译器将生成尽可能高效的"技巧"。当除数未知时,直到运行时,模需要更长的时间。