我认为诀窍在于以下内容(我将使用十进制进行演示,因为更容易理解,但原则应该相同)
假设您要计算 a*b mod 10000-1
,并且
a = 1234 = 12 * 100 + 34
b = 5432 = 54 * 100 + 32
现在 a*b = 12 * 54 * 10000 + 34 * 54 * 100 + 12 * 32 * 100 + 34 * 32
12 * 54 * 10000 = 648 * 10000
34 * 54 * 100 = 1836 * 100
12 * 32 * 100 = 384 * 100
34 * 32 = 1088
由于 x * 10000 ≡ x (mod 10000-1)
[1],因此第一项和最后一项变成了648+1088。第二项和第三项是“诀窍”的关键。请注意:
1836 = 18 * 100 + 36
1836 * 100 ≡ 18 * 10000 + 3600 ≡ 3618 (mod 10000-1).
本质上这是一个循环移位。计算648 + 3618 + 8403 + 1088的结果。同时请注意,在所有情况下,乘法的数字都小于10000(因为a < 100且b < 100),因此如果只能将2位数相乘并相加,则可以计算出结果。
在二进制中,计算方式类似。
从a和b开始,它们都是32位的。假设你想要对它们进行模2^31-1的乘法,但你只有一个16位的乘数(得到32位)。算法大致如下:
a = 0x12345678
b = 0xfedbca98
accumulator = 0
for (x = 0; x < 32; x += 16)
for (y = 0; y < 32; y += 16)
temp = ((a >> x) & 0xFFFF) * ((b >> y) & 0xFFFF)
total_bits_shifted = x + y
for (bits = 0; bits < total_bits_shifted + 32; bits += 31)
accumulator += (temp >> (bits - total_bits_shifted)) & 0x7FFFFFFF
if (accumulator > 0x7FFFFFFFF)
accumulator = (accumulator >> 31) + (accumulator & 0x7FFFFFFF);
现在已经很晚了,因此累加器部分可能无法正常工作。但我认为原则上是正确的。有人可以进行编辑以使其正确。
展开后,这也相当快,这应该是PRNG使用的方式,我猜测。
[1]: x*10000 ≡ x*(9999+1) ≡ 9999*x + x ≡ x (mod 9999)