【问题标题】:Efficient BigInteger multiplication modulo n in JavaJava中高效的BigInteger乘法模n
【发布时间】:2018-04-12 02:18:09
【问题描述】:

我可以计算两个 BigIntegers(比如 ab)模 n 的乘积。

这可以通过以下方式完成:

a.multiply(b).mod(n);

但是,假设abn的同阶,这意味着在计算过程中,一个新的BigInteger是正在计算,其长度(以字节为单位)为 ~ 2n

我想知道是否有更有效的实现可以使用。像 modMultiply 之类的东西,像 modPow 一样实现(我相信它不会计算功率,然后是模数)。

【问题讨论】:

  • 模数是素数吗?在这种情况下,可以利用它的某些属性来加快速度。

标签: java math biginteger


【解决方案1】:

我只能想到

a.mod(n).multiply(b.mod(n)).mod(n)

而且您似乎已经意识到这一点。

BigInteger 有一个toByteArray(),但在内部使用了ints。因此 n 必须非常大才能产生影响。也许在密钥生成密码代码中可能会有这样的工作。

此外,如果您考虑缩短乘法,您将得到如下结果:

public static BigInteger multiply(BigInteger a, BigInteger b, int mod) {
    if (a.signum() == -1) {
        return multiply(a.negate(), b, mod).negate();
    }
    if (b.signum() == -1) {
        return multiply(a, b.negate(), mod).negate();
    }

    int n = (Integer.bitCount(mod - 1) + 7) / 8; // mod in bytes.
    byte[] aa = a.toByteArray(); // Highest byte at [0] !!
    int na = Math.min(n, aa.length); // Heuristic.
    byte[] bb = b.toByteArray();
    int nb = Math.min(n, bb.length); // Heuristic.
    byte[] prod = new byte[n];
    for (int ia = 0; ia < na; ++ia) {
        int m = ia + nb >= n ? n - ia - 1 : nb; // Heuristic.
        for (int ib = 0; ib < m; ++ib) {
            int p = (0xFF & aa[aa.length - 1 - ia]) * (0xFF & bb[bb.length - 1 - ib]);
            addByte(prod, ia + ib, p & 0xFF);
            if (ia + ib + 1 < n) {
                addByte(prod, ia + ib + 1, (p >> 8) & 0xFF);
            }
        }
    }
    // Still need to do an expensive mod:
    return new BigInteger(prod).mod(BigInteger.valueOf(mod));
}

private static void addByte(byte[] prod, int i, int value) {
    while (value != 0 && i < prod.length) {
        value += prod[prod.length - 1 - i] & 0xFF;
        prod[prod.length - 1 - i] = (byte) value;
        value >>= 8;
        ++i;
    }
}

那段代码看起来并不诱人。 BigInteger 的问题是仅将内部值公开为 big-endian byte[],其中第一个字节是最重要的字节。

如果数字以 N 为底会更好。这不是不可想象的:如果 N 是 2 的幂,那么一些不错的优化是可行的。

(顺便说一句,代码未经测试 - 因为它似乎没有令人信服的更快。)

【讨论】:

    【解决方案2】:

    首先,坏消息:我找不到任何提供此功能的现有 Java 库。

    • 我找不到 任何 纯 Java 大整数库 ... 除了 java.math.BigInteger

    • GMP 库有 Java / JNI 包装器,但 GMP 也没有实现这一点。

    那么你有什么选择?

    • 也许我错过了一些纯 Java 库。

    • 也许有一些其他的本地 (C/C++) 大整数库支持此操作......尽管您可能需要编写自己的 JNI 包装器。

    • 复制java.math.BigInteger 的源代码并添加一个额外的自定义方法,您应该能够自己实现这样的方法。或者,您似乎可以extend它。


    话虽如此,我不确定在 Java 或任何其他语言中是否存在用于计算 a * b mod n 的“明显更快”的算法。 (除了特殊情况;例如,n 是 2 的幂)。

    具体来说,“Montgomery Reduction”方法对单个乘法步骤没有帮助。 (维基百科页面说:“由于数字必须转换为适合执行蒙哥马利步骤的特定形式,因此使用蒙哥马利步骤执行的单个模乘实际上比“幼稚”的效率略低。 ")

    因此,加速计算的最有效方法可能是使用 GMP 的 JNI 包装器。

    【讨论】:

    • 我也没有找到这样的东西。至少看起来我没有错过任何明显的事情。 :)
    • 巴雷特减少怎么样?我认为即使对于孤立的乘法,它也能提供加速。
    【解决方案3】:

    您可以使用通用数学,例如: (A*B) mod N = ((A mod N) * (B mod N)) mod N

    它可能更占用 CPU,但应该在 CPU 和内存之间进行选择,对吧?

    如果我们谈论的是模运算,那么确实可能需要蒙哥马利约简。虽然不知道任何开箱即用的解决方案。

    【讨论】:

    • 是密码问题,a和b已经小于n了。
    • 这个答案我也看不懂。
    • @Gari BN 抱歉,忘记了 A 和 B 已经小于 N 的部分。在这种情况下,似乎需要更多信息。就像可以分解成更小的乘数一样。
    【解决方案4】:

    您可以将 BigInteger 乘法编写为非常大的基数中的标准长乘法 - 例如,基数为 2^32。这相当简单。如果您只想要以 n 为模的结果,那么选择作为 n 因子的基数或 n 是因素。然后,您可以在执行计算时忽略除一个或几个最低阶结果(大)数字之外的所有数字,从而节省空间,也许还可以节省时间。

    当然,如果您事先知道n,那将是最实用的,但这种预先了解并不是必需的。如果 n 是 2 的幂,那就太好了,如果 n 既不是 2 的幂,也不小于系统算术单元直接处理的最大操作数,那就太麻烦了,但所有这些情况都可以原则上处理。

    但是,如果您必须专门针对 Java BigInteger 实例执行此操作,那么请注意,BigInteger 类本身未提供的任何方法都会产生内部和外部表示之间转换的开销。

    【讨论】:

      【解决方案5】:

      也许是这样的:

      static BigInteger multiply(BigInteger c, BigInteger x)
      {
          BigInteger sum = BigInteger.ZERO;
          BigInteger addOperand;
          for (int i=0; i < FIELD_ELEMENT_BIT_SIZE; i++)
          {
              if (c.testBit(i))
                  addOperand = x;
              else
                  addOperand = BigInteger.ZERO;
      
              sum = add(sum, addOperand);
      
              x = x.shiftRight(1);
          }
      
          return sum;
      }
      

      使用以下辅助函数:

      static BigInteger add(BigInteger a, BigInteger b)
      {
          return modOrder(a.add(b));
      }
      
      static BigInteger modOrder(BigInteger n)
      {
          return n.remainder(FIELD_ORDER);
      }
      

      但说实话,我不确定这是否真的有效,因为这些操作都不是就地执行的。

      【讨论】:

        猜你喜欢
        • 2016-04-17
        • 2020-10-25
        • 1970-01-01
        • 2020-04-01
        • 1970-01-01
        • 2018-10-15
        • 2021-03-25
        • 1970-01-01
        相关资源
        最近更新 更多