Karastuba算法实现高精度乘法

博主之前的文章 [C#]高精度算法 中,简述过基于循环高精度加法来实现高精度乘法的思路。
这一篇,将从另一个角度来实现这个功能。

背景 & 思想

Karastuba 博士在 1960 年提出了一个用于计算大整数乘法的算法,其思想是把两个大整数的乘法转化为若干次小规模的乘法和少量的加法。

一般来说,对于两个 n 位的大整数 x 和 y ,可以将其分解为如下的两部分:

x = x 1 1 0 n 2 + x 0 ,     0 ≤ x 0 < 1 0 n 2 x={x_1}^10^\frac{n}{2}+x_0,\ \ \ 0\leq x_0<10^\frac{n}{2} x=x1102n+x0,   0x0<102n y = y 1 1 0 n 2 + y 0 ,     0 ≤ y 0 < 1 0 n 2 y={y_1}^10^\frac{n}{2}+y_0,\ \ \ 0\leq y_0<10^\frac{n}{2} y=y1102n+y0,   0y0<102n

例如: 12345 = 123 x 1 0 2 10^2 102 + 45
其实质是利用欧几里得算式将一个整数分解成 m = qn + r 的形式。
因此,
x ∗ y = ( x 1 1 0 n 2 + x 0 ) ( y 1 1 0 n 2 + y 0 ) x * y = (x_1^10^\frac{n}{2}+x_0)(y_1^10^\frac{n}{2}+y_0) xy=(x1102n+x0)(y1102n+y0)
         = x 1 y 1 1 0 n + ( x 1 y 0 + x 0 y 1 ) 1 0 n 2 + x 0 y 0 \ \ \ \ \ \ \ \ = {x_1y_1}^10^n + (x_1y_0 + x_0y_1)^10^\frac{n}{2} + x_0y_0         =x1y110n+(x1y0+x0y1)102n+x0y0
这其中, 1 0 n 10^n 10n 可以通过位移来高效运算,原先的大整数乘法就可以分解为四次小规模的乘法和一些加法。
但是,实际运算中给出的两个数字不一定是等位数的,例如: 123 x 12345 。
假设 x 和 y 分别是 m 位和 n 位的大整数,
则:
x = x 1 1 0 m 2 + x 0 ,       0 ≤ x 0 < 1 0 m 2 x=x_1^10^\frac{m}{2} + x_0,\ \ \ \ \ 0\leq x_0 < 10^\frac{m}{2} x=x1102m+x0,     0x0<102m y = y 1 1 0 n 2 + y 0 ,       0 ≤ y 0 < 1 0 n 2 y=y_1^10^\frac{n}{2} + y_0,\ \ \ \ \ 0\leq y_0 < 10^\frac{n}{2} y=y1102n+y0,     0y0<102n
易得:
x y = ( x 1 1 0 m 2 + x 0 ) ( y 1 1 0 n 2 + y 0 ) xy=(x_1 10^\frac{m}{2} + x_0)(y_1 10^\frac{n}{2} + y_0) xy=(x1102m+x0)(y1102n+y0) = x 1 y 1 1 0 m + n 2 + x 1 y 0 1 0 m 2 + x 0 y 1 1 0 n 2 + x 0 y 0 =x_1 y_1 10^\frac{m+n}{2} + x_1 y_0 10^\frac{m}{2} + x_0 y_1 10^\frac{n}{2} + x_0 y_0 =x1y1102m+n+x1y0102m+x0y1102n+x0y0
那么我们只要反复迭代多项式的每一项 x i y j x_i y_j xiyj , 直到其中一个乘数只有一个为止。

实现

分析:x=123, y=321。用上标表示迭代的次数,则 x · y 的过程如下:
123 = 12 x 1 0 1    3          321 = 32 x 1 0 1    1 123=12x10^1\ \ 3\ \ \ \ \ \ \ \ 321=32x10^1\ \ 1 123=12x101  3        321=32x101  1
x 1 ① = 12 ,   x 0 ① = 3       y 1 ① = 32 ,   y 0 ① = 1 x_1^①=12,\ x_0^①=3\ \ \ \ \ y_1^①=32,\ y_0^①=1 x1=12, x0=3     y1=32, y0=1
那么,
x y = x 1 ① y 1 ① 1 0 2 + x 1 ① y 0 ① 1 0 1 + x 0 ① y 1 ① 1 0 1 + x 0 ① y 0 ① xy=x_1^①y_1^①10^2 + x_1^①y_0^①10^1 + x_0^①y_1^①10^1 + x_0^①y_0^① xy=x1y1102+x1y0101+x0y1101+x0y0
     = ( 12 ∗ 32 ) 1 0 2 + ( 12 ∗ 1 ) 1 0 1 + ( 3 ∗ 32 ) 1 0 1 + 3 ∗ 1 \ \ \ \ =(12 * 32)10^2+(12 * 1)10^1+(3 * 32)10^1+3 * 1     =(1232)102+(121)101+(332)101+31
其中,12x1、3x32、3x1 都出现了有一个乘数是 1 的情况,那么就不必继续迭代,可以直接返回结果。
第二次迭代,实际就是对 12 、32 再次进行 karastuba 分解而已,原理是一样的。
结合思路与迭代经验,可以写出这样的代码:

def karastuba(x,y):
    if x==0 or y==0:return 0
    m,n=len(str(x)),len(str(y))
    if m==1 or n==1:return x * y
    m//=2;n//=2
    x1,x0,y1,y0=x//(10**m),x%(10**m),y//(10**n),y%(10**n)
    x1y1,x1y0,x0y1,x0y0=karastuba(x1,y1),karastuba(x1,y0),karastuba(x0,y1),karastuba(x0,y0)
    return x1y1*(10**(m+n)) + x1y0*(10**m) + x0y1*(10**n) + x0y0

使用时,调用 karastuba 函数即可。
受限于篇幅关系,支持符号和小数点的算法可以自行探索。
另外,我们可以用这样的代码来测试:

def test(x,y):
    rt = karastuba(x,y)
    print(str(x)+" * "+str(y)+" = "+str(rt), rt == x * y)
    
if __name__ == "karastuba":
    test(123,321)
    test(123456789,987654321)

最后,保存,运行,输出结果:

>>> from karastuba import *
123 * 321 = 39483 True
123456789 * 987654321 = 121932631112635269 True

参考资料

《程序员数学从零开始》,孙博 (@我是 8 位的)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醉月酿星河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值