Java中的高效BigInteger乘法模数

时间:2014-08-25 11:12:14

标签: java math biginteger

我可以计算两个BigIntegers的乘法(比如 a b )模数 n

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

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

但是,假设 a b n 的顺序相同,则意味着在计算过程中,新的BigInteger是正在计算,其长度(以字节为单位)为〜 2n

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

5 个答案:

答案 0 :(得分:4)

我只能想到

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

你似乎已经意识到了这一点。

BigInteger有一个toByteArray(),但内部int被使用。因此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的问题是只将内部值暴露为大端byte[],其中第一个字节是最重要的字节。

更好的方法是将数字设为基数N. 这不是不可想象的:如果N是2的幂,那么一些不错的优化是可行的。

(BTW代码未经测试 - 因为它似乎没有令人信服的更快。)

答案 1 :(得分:2)

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

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

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

那你有什么选择?

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

  • 也许有一些其他原生(C / C ++)大整数库支持这个操作......虽然你可能需要编写自己的JNI包装器。

  • 您应该能够通过复制 java.math.BigInteger的源代码并添加额外的自定义方法,为自己实现这样的方法。或者,看起来你可以extend


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

具体而言,“蒙哥马利减少”方法对单个乘法步骤没有帮助。 (维基百科页面说:“因为数字必须转换为适合执行蒙哥马利步骤的特定形式,所以使用蒙哥马利步骤执行的单个模块化乘法实际上比”天真“步骤效率略低。 “

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

答案 2 :(得分:1)

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

它可能是CPU密集型的,但是应该在CPU和内存之间进行选择,对吧?

如果我们谈论模块化算术,那么确实蒙哥马利减少可能是你需要的。但是,不知道任何开箱即用的解决方案。

答案 3 :(得分:0)

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

如果你事先知道 n ,这是最实用的,但这种预先知识并不重要。如果 n 是2的幂,那就特别好了,如果n既不是2的幂也不是小于系统直接处理的最大操作数,那就相当麻烦#&# 39;算术单位,但所有这些案件原则上都可以处理。

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

答案 4 :(得分:0)

也许这个:

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);
}

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

相关问题