简化表达式k / m%n

时间:2010-06-10 05:33:31

标签: algorithm language-agnostic math

简单的问题是,是否可以通过更便宜的操作来简化(或替换除法或模数)

(k/m)%n

其中变量是整数,运算符是C样式除法和模运算符。

让我稍微重新解释一下问题,除了变量是base2的情况,在什么条件下(例如某些变量可能是常数)可以简化表达式(或者使用base2操作部分重新表达)来删除除法或模数?

这是我学习数论的方法,特别是base2技巧,而不是在性能优化中练习

谢谢

5 个答案:

答案 0 :(得分:3)

对于任意精度整数,我建议查看http://documents.epfl.ch/users/k/ka/kaihara/www/papers/ModMulDiv_Binary.pdf

它提供了一种硬件方法,但它提供了可以适应的伪代码。

答案 1 :(得分:3)

明显的优化:

  1. m == 1:答案只是k % m
  2. n == 1:答案总是0
  3. m是2的幂:例如如果m为4,则可以使用(k >> 2) % n;
  4. n是2的幂:表达式变为(k / m) & (n - 1);
  5. 检查#1和#2是微不足道的。

    使用以下方法检查2的权力:

    void isPowerOfTwo(unsigned int x)
    {
        return x & (x - 1) == 0;
    }
    

答案 2 :(得分:3)

对于具有小常数分母的除法,您可以使用类似的东西。

k/m=k*(1/m)
x=(1<<16)/m
k/m=(k*x)>>16

根据输入,答案可能不准确。

对于具有小奇数常数分母的除法,您可以使用multiplicative inverse。以下常量适用于32位除法。

3   2863311531      11  3123612579
5   3435973837      13  3303820997
7   3067833783      15  4008636143
9    954437177      17  4042322161

x/11 == x*3123612579 % 2^32

% 2^32当然在32位整数上是免费的。采用这个偶数来分解两个并在以后应用它们。

x/44 == (x*3123612579 % 2^32) >> 2

Hackers Delight有一章关于整数除法。

两个幂的简单模数和除法。

x%m == x&(m-1)
x/m == x>>log2(m) // assumes log2(m) is known, not calculated

答案 3 :(得分:1)

加入Peter Alexander的回答

0)当然m!= 0&amp;&amp; n!= 0是前提条件......

1)k&lt; m:答案总是0

2)k == m:答案总是1(除非n也是1,见5)。

3)k / m < n:答案是k / m

4)k&lt; (m * n):答案总是k / m。       由于m * n,这种特殊条件不太有利于优化       不会被重用,它不应该比模数快得多       除非m和/或n是2的幂,否则你仍然可以更好地使用       7.和/或8.

参考添加彼得亚历山大:

5)m == 1:答案只是k%n。

6)n == 1:答案总是为0。

7)m是2的幂:例如如果m为4,则可以使用(k>&gt;&gt; 2)%n;

8)n是2的幂:表达式变为(k / m)&amp; (n - 1);

答案 4 :(得分:0)

我认为这取决于。 一个正确的算术移位会让你除以2,并且通过一些额外的算术,你可以将它划分为任何数字的除法。 同样地,我认为你可以对模运算符做同样的事情。 实际上,除非你的处理器缺少必要的硬件,否则我不会想到你会获得任何东西。

修改的 实际上在考虑这个问题时,严格来说需要更多的思考,对于负数,签名班次不能作为2的除法。

如果我没记错的话,算术右移的行为在负数的C标准中是未定义的(它涉及2的幂),因此依赖于编译器。

如果我们只是考虑数论的逻辑那么那就是另一回事了。让我考虑一下。