对于c = 2 ^ N + -1,快速计算(a * b)mod c

时间:2009-04-18 08:29:46

标签: math integer modulo prng knuth

在32位整数数学中,add和multiply的基本数学运算隐式计算mod 2 ^ 32,这意味着你的结果将是加或乘的最低位。

如果要使用不同的模数计算结果,您当然可以使用不同语言的任意数量的BigInt类。并且对于值a,b,c< 2 ^ 32你可以用64位长的int来计算中间值,并使用内置的%运算符来减少到右边的答案

但是我被告知当C的形式为(2 ^ N)-1或(2 ^ N)+1时,有一些特殊的技巧可以有效地计算a * b mod C,它们不使用64比特数学或BigInt库是非常有效的,比任意模数评估更有效,并且还适当地计算如果包括中间乘法通常会溢出32位int的情况。

不幸的是,尽管听说这种特殊情况有快速评估方法,但实际上我还没有找到该方法的描述。 “那不是在Knuth吗?” “这不就是维基百科上的某个地方吗?”是我听到的咕。声。

显然,随机数生成器中的常用技术是a * b mod 2147483647的乘法,因为2147483647是等于2 ^ 31 -1的素数。

所以我会问专家。什么是这个聪明的特殊情况乘法与mod方法,我找不到任何讨论?

6 个答案:

答案 0 :(得分:10)

我认为诀窍如下(我将在10号基地进行,因为它更容易,但原则应该成立)

假设您正在乘以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的结果。并且还要注意,在所有情况下,乘以的数字都是&lt; 10000(因为&lt; 100和b <100),所以如果您只能将多个2位数字组合在一起,并且添加它们,则可以计算出来。

在二进制文件中,它会以类似的方式运作。

以a和b开头,均为32位。假设你想将它们乘以mod 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)
         // do the multiplication, 16-bit * 16-bit = 32-bit
         temp = ((a >> x) & 0xFFFF) * ((b >> y) & 0xFFFF)

         // add the bits to the accumulator, shifting over the right amount
         total_bits_shifted = x + y
         for (bits = 0; bits < total_bits_shifted + 32; bits += 31)
             accumulator += (temp >> (bits - total_bits_shifted)) & 0x7FFFFFFF

         // do modulus if it overflows
         if (accumulator > 0x7FFFFFFFF)
             accumulator = (accumulator >> 31) + (accumulator & 0x7FFFFFFF);

已经很晚了,所以累加器部分可能无法正常工作。我认为原则上它是正确的。有人可以随意编辑它以使其正确。

展开,这也是非常快的,这是PRNG使用的,我猜。

[1]: x*10000 ≡ x*(9999+1) ≡ 9999*x + x ≡ x (mod 9999)

答案 1 :(得分:3)

假设您可以将* b计算为p*2^N+q。这可能需要64位计算,或者您可以将a和b拆分为16位部分并以32位计算。

a*b mod 2^N-1 = p+q mod 2^N-1以后2^N mod 2^N-1 = 1

a*b mod 2^N+1 = -p+q mod 2^N+1以及2^N mod 2^N+1 = -1

在这两种情况下,2^N-12^N+1都没有划分。

答案 2 :(得分:2)

快速搜索了这个:http://home.pipeline.com/~hbaker1/AB-mod-N.pdf。不幸的是,对于我来说,仅仅用简化的公式来写出足够的意义已经太晚了,但它可能在那篇论文中。

答案 3 :(得分:1)

您可以使用Montgomery reduction(还有其他descriptions)来降低模块化乘法计算的成本,而不是在每个步骤中进行模块缩减。但是,这仍然不使用N的属性加/减2的幂。

答案 4 :(得分:1)

您正在寻找的身份是x mod N = (x mod 2^q)- c*floor(x/2^q),假设N = 2^q + c且c是任何整数(但通常为±1)。

你可能想阅读第9.2.3节:“特殊形式的模数”“Prime Numbers:A Computational Perspective”中由Richard Crandall和Carl Pomerance阅读。除了理论之外,它还包含用于实现上述关系的算法的伪代码。

答案 5 :(得分:0)

我在这个主题上找到了rather extensive page,不仅讨论了算法,还讨论了问题和解决方案的具体历史以及人们使用解决方案的方式。