我刚刚听说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
”。什么是“简单的调整”?
答案 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
,其中包含数字x0
,x1
,...,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)
已经解释了模数,但是,让我们重新概括一下。
要查找k
模2^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
位“将第一个设置位向右移动n
或n-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)