有没有简单的方法来做模数2 ^ 32-1的操作?

时间:2012-03-25 01:01:07

标签: algorithm modulus

我刚刚听说x mod (2^32-1)x / (2^32-1)会很容易,但是怎么样?

计算公式:

x n =(x n-1 + x n-1 / b)mod b。

b = 2^32,简单,x%(2^32) == x & (2^32-1);和x / (2^32) == x >> 32。 (这里的^不是XOR)。当b = 2 ^ 32 - 1时如何做到这一点。

在页面https://en.wikipedia.org/wiki/Multiply-with-carry中。他们说“arithmetic for modulus 2^32 − 1 requires only a simple adjustment from that for 2^32”。什么是“简单的调整”?

4 个答案:

答案 0 :(得分:6)

(此答案仅处理mod案例。)

我假设x的数据类型超过32位(这个答案实际上适用于任何正整数)并且它是正数(负面情况只是-(-x mod 2^32-1)),因为如果它最多32位,问题可以通过

来回答
x mod (2^32-1) = 0 if x == 2^32-1, x otherwise
x / (2^32 - 1) = 1 if x == 2^32-1, 0 otherwise

我们可以在基数2 ^ 32中写x,其中包含数字x0x1,...,xn。所以

  x = x0 + 2^32 * x1 + (2^32)^2 * x2 + ... + (2^32)^n * xn

这使我们在进行模数时​​答案更清晰,因为2^32 == 1 mod 2^32-1。那是

  x == x0 + 1 * x1 + 1^2 * x2 + ... + 1^n * xn (mod 2^32-1)
    == x0 + x1 + ... + xn (mod 2^32-1)

x mod 2^32-1与基数2 ^ 32位的总和相同! (我们不能放弃mod 2 ^ 32-1)。我们现在有两种情况,要么总和在0到2 ^ 32-1之间,要么更大。在前者,我们完成了;在后来,我们可以重复,直到我们在0到2 ^ 32-1之间。获取base 2 ^ 32中的数字很快,因为我们可以使用按位运算。在Python中(这不处理负数):

def mod_2to32sub1(x):
    s = 0 # the sum

    while x > 0: # get the digits
        s += x & (2**32-1)
        x >>= 32

    if s > 2**32-1:
        return mod_2to32sub1(s)
    elif s == 2**32-1:
        return 0
    else:
        return s

(这非常容易概括为x mod 2^n-1,事实上,您只需在此答案中用n替换任何32的出现。)

(编辑:添加elif子句以避免mod_2to32sub1(2**32-1)上的无限循环.EDIT2:将^替换为** ... oops。)

答案 1 :(得分:2)

因此,您使用“规则”2 32 = 1进行计算。通常,2 32 + x = 2 x 。你可以用指数模32来简化2 a 。例如:2 66 = 2 2

您可以用二进制表示任何数字,然后降低指数。示例:数字2 40 + 2 38 + 2 20 + 2 + 1可简化为2 8 + 2 6 + 2 20 + 2 + 1。

一般情况下,你可以每32次幂对2进行分组,并将所有指数“降级”为32。

对于64位字,数字可以表示为

2 32 A + B

其中0 <= A,B <= 2 32 -1。通过按位操作获得A和B很容易。

所以你可以把它简化为A + B,它要小得多:最多2 33 。然后,检查这个数字是否至少为2 32 -1,并在这种情况下减去2 32 - 1。

这避免了昂贵的直接划分。

答案 2 :(得分:1)

已经解释了模数,但是,让我们重新概括一下。

要查找k2^n-1的余数,请写

k = a + 2^n*b,  0 <= a < 2^n

然后

k = a + ((2^n-1) + 1) * b
  = (a + b) + (2^n-1)*b
  ≡ (a + b) (mod 2^n-1)

如果a + b >= 2^n,请重复,直到余数小于2^n,如果这导致a + b = 2^n-1,请将其替换为0.每个“向右移动n并添加到最后n位“将第一个设置位向右移动nn-1个位置(除非k < 2^(2*n-1),当移位后的第一个设置位 - 和 - add可以是2^n位。因此,如果类型的宽度与n相比较大,则需要多次转换 - 考虑128位类型和n = 3,对于大型k,您需要超过40次转换。为了减少所需的班次,您可以利用

这一事实
2^(m*n) - 1 = (2^n - 1) * (2^((m-1)*n) + 2^((m-2)*n) + ... + 2^(2*n) + 2^n + 1),

我们只对所有2^n - 1使用2^(m*n) - 1除以m > 0。然后,您移动n的倍数,该倍数大约是该步骤值可以具有的最大位长度的一半。对于128位类型的上述示例和模7的余数(2^3 - 1),最接近的3到128/2的倍数是63和66,第一次移位63位

r_1 = (k & (2^63 - 1)) + (k >> 63) // r_1 < 2^63 + 2^(128-63) < 2^66

获得最多66位的数字,然后移位66/2 = 33位

r_2 = (r_1 & (2^33 - 1)) + (r_1 >> 33) // r_2 < 2^33 + 2^(66-33) = 2^34

最多可达到34位。接下来移位18位,然后是9,6,3

r_3 = (r_2 & (2^18 - 1)) + (r_2 >> 18) // r_3 < 2^18 + 2^(34-18) < 2^19
r_4 = (r_3 & (2^9 - 1)) + (r_3 >> 9)   // r_4 < 2^9 + 2^(19-9) < 2^11
r_5 = (r_4 & (2^6 - 1)) + (r_4 >> 6)   // r_5 < 2^6 + 2^(11-6) < 2^7
r_6 = (r_5 & (2^3 - 1)) + (r_5 >> 3)   // r_6 < 2^3 + 2^(7-3) < 2^5
r_7 = (r_6 & (2^3 - 1)) + (r_6 >> 3)   // r_7 < 2^3 + 2^(5-3) < 2^4

如果r_7 >= 2^3 - 1足够,现在只需一次减法。要计算b位类型中的k % (2^n -1),需要O(log 2 (b / n))移位。

同样获得商,我们再写

k = a + 2^n*b,  0 <= a < 2^n
  = a + ((2^n-1) + 1)*b
  = (2^n-1)*b + (a+b),

所以k/(2^n-1) = b + (a+b)/(2^n-1),我们会在a+b > 2^n-1期间继续。遗憾的是,我们不能通过移动和屏蔽大约一半的宽度来减少工作,因此只有当n不比类型的宽度小很多时,该方法才有效。

n不太小的快速案例代码:

unsigned long long modulus_2n1(unsigned n, unsigned long long k) {
    unsigned long long mask = (1ULL << n) - 1ULL;
    while(k > mask) {
        k = (k & mask) + (k >> n);
    }
    return k == mask ? 0 : k;
}

unsigned long long quotient_2n1(unsigned n, unsigned long long k) {
    unsigned long long mask = (1ULL << n) - 1ULL, quotient = 0;
    while(k > mask) {
        quotient += k >> n;
        k = (k & mask) + (k >> n);
    }
    return k == mask ? quotient + 1 : quotient;
}

对于n是类型宽度的一半的特殊情况,循环最多运行两次,因此如果分支很昂贵,则最好展开循环并无条件地执行循环体两次。

答案 3 :(得分:0)

不是。你必须听到的是x mod 2 ^ n和x / 2 ^ n更容易。 x / 2 ^ n可以被执行为x>&gt; n,并且x mod 2 ^ n,do x&amp;(1&lt;&lt; n-1)