我正在Scala(我的选择)中实现Karatsuba multiplication的在线课程。考虑到该算法意味着要乘以大数,我选择了BigInt
类型,该类型由Java BigInteger支持。我想有效地实现该算法,该算法使用的是基数为10的算法,复制自下面的Wikipedia:
procedure karatsuba(num1, num2)
if (num1 < 10) or (num2 < 10)
return num1*num2
/* calculates the size of the numbers */
m = max(size_base10(num1), size_base10(num2))
m2 = floor(m/2)
/* split the digit sequences in the middle */
high1, low1 = split_at(num1, m2)
high2, low2 = split_at(num2, m2)
/* 3 calls made to numbers approximately half the size */
z0 = karatsuba(low1, low2)
z1 = karatsuba((low1 + high1), (low2 + high2))
z2 = karatsuba(high1, high2)
return (z2 * 10 ^ (m2 * 2)) + ((z1 - z2 - z0) * 10 ^ m2) + z0
鉴于BigInteger
在内部表示为int[]
,如果我可以根据m2
计算int[]
,则可以使用位移来提取下限和数字的一半。同样,最后一步也可以通过移位来实现。
但是,说起来容易做起来难,因为我似乎无法完全理解逻辑。例如,如果最大数量为999
,则二进制表示形式为1111100111
,下半部为99 = 1100011
,上半部为9 = 1001
。如何获得上述拆分?
注意:
有existing question展示了如何在BigInteger
上使用算术实现,而不是移位。因此,我的问题不是重复的。
答案 0 :(得分:1)
要能够使用移位进行拆分和重组,基数必须为2的幂。如链接的答案中所述,使用两个本身可能是合理的。然后可以使用bitLength
直接找到输入的“长度”,并且拆分可以实现为:
// x = a + 2^N b
BigInteger b = x.shiftRight(N);
BigInteger a = x.subtract(b.shiftLeft(N));
N
的大小a
以位为单位。
考虑到BigInteger
是用32位分支实现的,因此以2³²为基础是有意义的,确保大的移位仅涉及整数的移动,而不涉及{{ 1}}的值在1到31之间移动。这可以通过将BigInteger
舍入为32的倍数来实现。
此行中的特定常数
N
鉴于该评论,应该不太值得信任。为了提高性能,应该有一个 some 约束,否则递归拆分会变得太深。例如,当数字的大小等于或小于32时,乘以显然更好,但是一个好的临界值可能要高得多。在if (N <= 2000) return x.multiply(y); // optimize this parameter
的{{3}}中,截止值是用肢体的数量而不是位数来表示的,并设置为80(即2560位)-它还具有另一个阈值,在该阈值之上它会切换为3路Toom-Cook乘法而不是唐津乘法。