【问题标题】:Computing (a*b) mod c quickly for c=2^N +-1对于 c=2^N +-1 快速计算 (a*b) mod c
【发布时间】:2009-04-18 08:29:45
【问题描述】:

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

如果您想用不同的模数计算结果,您当然可以使用不同语言的任意数量的 BigInt 类。对于 a,b,c

但有人告诉我,当 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 方法是什么?

【问题讨论】:

    标签: math integer modulo prng knuth


    【解决方案1】:

    我认为诀窍如下(我打算以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 的结果。还要注意,在所有情况下,相乘的数字都

    在二进制中,它的工作原理类似。

    以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)

    【讨论】:

    • 仍然不明白这背后的数学是我放弃大学数学的原因......
    • 嗯,这有点像除以 9 (10-1) 时得到余数。您只需将数字相加即可。现在在这种情况下,您不是以 10 为底或以 2 为底,而是“底”2^N
    【解决方案2】:

    假设您可以将 a*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 都没有划分。

    【讨论】:

      【解决方案3】:

      快速搜索一下:http://home.pipeline.com/~hbaker1/AB-mod-N.pdf。不幸的是,我已经太晚了,我无法理解这一点,只写简化公式,但它可能在那篇论文中的某个地方。

      【讨论】:

      • 论文使用浮点运算而不是N的性质使计算有效。我自己对浮点计算有点紧张,但还没有更深入地检查它……它可能工作得很好。
      • 趣味论文,值得一读!对于任意模值,这是一种更通用的方法。不幸的是,作为计算的一部分,它会将值转换为 64 位双精度值。一般来说,这可能是一种非常有效的计算,但对于特殊的 c=2^N +-1 情况,还有一些更快的方法。无论如何 +1 投票只是因为它是一个很棒的链接!
      【解决方案4】:

      除了每一步都做模减,可以使用Montgomery reduction(还有其他descriptions)来降低模乘计算的成本。不过,这仍然没有使用 N 的属性是加/减 2 的幂。

      【讨论】:

        【解决方案5】:

        您要查找的身份是 x mod N = (x mod 2^q)- c*floor(x/2^q),因为 N = 2^q + c 和 c 是任意整数(但通常为 ±1)。

        您可能需要阅读 Richard Crandall 和 Carl Pomerance 的“素数:计算视角”中的 第 9.2.3 节:“特殊形式的模”。除了理论,它还包含实现上述关系的算法的伪代码。

        【讨论】:

        • 你确定这是正确的吗?例如。 wolfram alpha raw modulus calculation exampleyour formula example 对于 2^16+168519263247*1564832
        • 我找到了你提到的那本书,我想你忘记了最后一个重要部分:计算的第二部分应该添加mod N,例如:x mod N = (x mod 2^q)- c*floor(x/2^q) mod N (example) .由于这仍然包含mod N,我不确定使用这个公式的好处是什么(?)
        • 你可以递归地继续应用公式,直到c*floor(x/2^q) &lt; N
        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 2012-01-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-21
          • 1970-01-01
          相关资源
          最近更新 更多