如何使用位操作实现唐津乘法

时间:2018-11-04 20:20:11

标签: java algorithm bit-manipulation biginteger karatsuba

我正在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上使用算术实现,而不是移位。因此,我的问题不是重复的。

1 个答案:

答案 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乘法而不是唐津乘法。