使用数组完成两个超长正整数的乘法_深入 Python (7) Karatsuba 实现长整数乘法

c41e11eb1bf6736ce2091a53f81fe3d3.png

Python 的长整数乘法使用了 Karatsuba 算法,昨天写的比较粗,今天仔细研究了下它的实现,真是妙啊。

多项式乘法

在一切开始之前,需要回顾多项式乘法公式:

(a + b)(c + d) = ac + ad + bc + bd

这是基本的初等数学知识,Karatsuba 算法的实现本质上就是对这个公式的变形。

长整数切分

为了计算长整数的乘法,一种典型的思路是使用分治思想,将长整数切分为较短的部分计算。这里需要注意的是,计算机在计算一个数的 2^n 倍非常快,只需要将内存左移 n 位即可。

对于一个无符号整数 A,可以将其切分高低两部分,假设将其切分为高低 8 位:

c21906edb3e4b9c935883acbe7d8b748.png

那么 A 就可以表示为

cd0d9824bd2937cf212c9d46c991e74e.png

,这里 n = 8。实际上 n 可以是任意正整数,对于长整数,为了便于计算,通常将 n 设置为较长整数宽度的一半。

两个长整数的乘积

由于我们可以将两个长整数按照低位均为 n 位切分:

a9ad13798ce1e6f725eb8964a7cdd42a.png

此时 A * B 就是一个多项式乘法:

b86ae35a8553e0f7a97a485f1667d8b6.png

这里需要注意一下,

c1a1e192695507c91425e404951af53c.png

,回顾前面提到的乘法实现,2^(n + n) 相当于将内存中的值整体左移 2n 位。

Python 中的 Karatsuba 乘法实现

首先 Python 申请一个长度为 sizeA + sizeB 的内存 Result,用于保存最终的结果,这一个连续区域被划分为三部分:

45a15cc8ead695e2d7a3e443e31fafae.png

由低到高分别是宽度为 n、n 及剩余的区间,如果对较浅色区域加 X,实际上相当于 Result + X * 2^n,在较深区域加 Y,相当于 Result + Y * 2^(2n),这两项刚好是上一部分讲到的多项式乘法的二次项和一次项。

完成结果对象的创建后,首先将 A、B 分割为低位宽度为 n 的两部分,先计算 A_hi * B_hi​ 并将结果保存到 深色区域,对照上一节中的公式,此时

c3d4736c04b770bc602b64ff82e43cf9.png

再计算 A_lo​∗B_lo​,结果放在最低 n 位,此时

9a1b77a7e9761a43b79165291970f5a7.png

,二次项和常数项都有了,下面是计算中间的一次项。

之后分别将

c788c63e88d4240617537380aec961a0.png

的结果加到左移 n 位的位置,内存看起来是这样:

7e2bdf61fa838286640af0095b16be34.png

此时

dbf237c7d2e5b4e41dc944e0939bf8c9.png

可以看到这种内存布局的巧妙之处,三段内存独立求和,假如存在进位,刚好自动计入更高分片。

对比完整公式,还欠缺一次项的 K。

最后在左移 n 位的位置加 K,就得到了最终的乘法结果。

关注我,了解程序员的烧脑日常,还有开源 Python 教程。

de1a00291836d858c5a4e55c362b3851.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值