我可以计算两个BigIntegers的乘法(比如 a 和 b )模数 n 。
这可以通过以下方式完成:
a.multiply(b).mod(n);
但是,假设 a 和 b 与 n 的顺序相同,则意味着在计算过程中,新的BigInteger是正在计算,其长度(以字节为单位)为〜 2n 。
我想知道我是否可以使用更高效的实现。类似modMultiply的东西就像modPow一样实现(我认为它不会计算功率,然后是模数)。
答案 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);
}
说实话,我不确定这是否真的有效,因为这些操作都不是就地执行的。