整数的故事(4)——Karastuba算法
我们在小学就学过用竖式计算两个多位数的乘法:
这个过程简单而繁琐,没有最强大脑的普通大众通常是用计算器代替的。然而对于超大整数的乘法,计算器也未必靠得住,它还存在“溢出”一说。这就需要我们自行编写算法了。
竖式算法
虽然对于Python来说,不必太过关心整数的长度和溢出问题,但对于其它编程语言就未必了。这里我们暂且抛开语言本身的特性,只关注算法本身。假设输入的两个长整数x和y,它们的乘积将会溢出,所以需要将乘积转换成字符串,根据乘法竖式的运算规则,很容易写出下面的代码:
# 竖式法计算两个整数相乘
def multi(x, y):
x_str = str(x)
y_str = str(y)
x_len = len(x_str)
y_len = len(y_str)
z = [0] * (x_len + y_len) for i in range(x_len):
for j in range(y_len):
z[x_len - i - 1 + y_len - j - 1] += int(x_str[i]) * int(y_str[j]) for i in range(len(z) - 1):
if z[i] >= 10:
z[i + 1] += (int(z[i]) // 10)
z[i] = int(z[i]) % 10 return list2str(z) # 将z转换成字符串并删除左侧的0
def list2str(z):
result = [str(i) for i in z]
return ''.join(result[::-1]).lstrip('') # 打印运行结果
def paint(a, b):
print('{0} * {1} = {2}'.format(a, b, multi(a, b))) if __name__ == '__main__':
paint(123,321)
paint(123,456)
paint(123456789000, 987654321000)
代码中9~11行用两个循环模拟了乘法计算的过程,以123×321为例,在循环结束后z将存储下面的数据:
11行的for循环是处理进位问题。最后将列表转换为字符串,再去掉多余的0,打印结果:
Karastuba算法
竖式乘法偏向于使用蛮力,Karastuba博士在1960年提出了一个更简单的算法,其思想是把两个大整数的乘法转化为若干次小规模的乘法和少量的加法,这就是Karastuba算法。
对于两个n位的大整数x和y,可以把x和y分解成两部分:
例如:
是不是有点似成相识?没错,这实际上是利用了欧几里德算式将一个整数分解成m=qn+r的形式。现在x和y的乘积可以表示为:
这就把原来的大整数乘法变成了四次效较小规模的乘法(其中10n的运算可以通过位移高效处理)和少量加法。上式还可以更进一步:
看起来更复杂了,但是对于计算机来说,x1y1和x0y0已经计算过了,不需要再次计算。x1y0+x0y1被转换成一次乘法和少量的加法,多一个加法运算对时间复杂度没有影响,而减少一个乘法却能减少时间复杂度。对每一个乘法都进行类似的分解,反复迭代xiyi,直到其中一个乘数只有1位为止。按照这种思路可以编写新的乘法运算代码:
# karastuba算法计算两个n位的大整数乘法, x >=0, y >= 0
def karastuba(x, y, n):
if x == 0 or y == 0:
return 0
elif n == 1:
return x * y k = n // 2
x1 = x // (10 ** k)
x0 = x % (10 ** k)
y1 = y // (10 ** k)
y0 = y % (10 ** k)
z0 = karastuba(x0, y0, k) # 计算x0y0
z1 = karastuba(x1, y1, k) # 计算x1y1
z2 = karastuba((x1 + x0), (y0 + y1), k) - z1 - z0 return z1 * (10 ** n) + z2 * (10 ** k) + z0
然而运行时会发现这段代码很难生效,原因是计算时要求的环境太过理想——每次迭代时xi和yi的位数都必须相同。这就需要重新审视Karastuba算法,看看非理想状态下是如何计算的。
假设x和y分别是m位和n位的大整数,x和y可以这样分解:
反复迭代xiyi,直到其中一个乘数只有1位为止。
示例: 123×321 = ?
现在可以编写能够正确运行的大整数乘法代码:
def karastuba(x, y):
''' karastuba算法计算两个n位的大整数乘法, x >=0, y >= 0 '''
if x == 0 or y == 0:
return 0
m, n = len(str(x)), len(str(y)) # x和y的位数
if m == 1 or n == 1: # 如果x或y只有1位,直接计算结果
return x * y
m //= 2
x1, x0 = x // (10 ** m), x % (10 ** m) # 分解x
n //= 2
y1, y0 = y // (10 ** n), y % (10 ** n) # 分解y
# 迭代分解够的4个较小规模的乘法
x1y1 = karastuba(x1, y1)
x1y0 = karastuba(x1, y0)
x0y1 = karastuba(x0, y1)
x0y0 = karastuba(x0, y0)
return x1y1 * (10 ** (m + n)) + x1y0 * (10 ** m) + x0y1 * (10 ** n) + x0y0 def paint(x, y):
''' 在控制台打印karastuba(x, y)的运行结果 '''
print('{0} * {1} = {2}, ({3})'.format(x, y, karastuba(x, y), x * y)) if __name__ == '__main__':
paint(123, 321)
paint(123456789, 987456)
paint(1234567891234567, 1234567891234567)
先看看windows计算器下1234567891234567×1234567891234567的运行结果:
计算器已经无法给出精确的结果,但karastuba没有问题:
对于非10进制整数,Karastuba算法依然适用。
作者:我是8位的
整数的故事(4)——Karastuba算法的更多相关文章
- 用Java实现在【520,1314】之间生成随机整数的故事
做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 在未来城市工作的的程序员小木,做了一个梦,梦到自己在塔鲁姆的街道上看到一个姑娘,这个姑娘从远处走向他,脸上带着微笑.让小木 ...
- C语言实现整数数组的逆置算法
读入100个整数到一个数组中,写出实现该数组进行逆置的算法. 方法一: 假设100个整数读入到数组a中,算法f1的思想是分别从数组两端依次将对应数进行交换,即a[i]与a[100 - i - 1]进行 ...
- png的故事:隔行扫描算法
转载自AlloyTeam:http://www.alloyteam.com/2017/06/the-story-of-png-deinterlacing-algorithm/ 前言 前文已经讲解过如何 ...
- php取两个整数的最大公约数算法大全
php计算两个整数的最大公约数常用算法 <?php//计时,返回秒function microtime_float (){ list( $usec , $sec ) = explode ( &q ...
- 素数算法(Prime Num Algorithm)
素数算法(Prime Num Algorithm) 数学是科学的皇后,而素数可以说是数学的最为核心的概念之一.围绕素数产生了很多伟大的故事,最为著名莫过于哥德巴赫猜想.素数定理和黎曼猜想(有趣的是,自 ...
- 十大经典排序算法总结(JavaScript描述)
前言 读者自行尝试可以想看源码戳这,博主在github建了个库,读者可以Clone下来本地尝试.此博文配合源码体验更棒哦~~~ 个人博客:Damonare的个人博客 原文地址:十大经典算法总结 这世界 ...
- (转)神经网络和深度学习简史(第一部分):从感知机到BP算法
深度|神经网络和深度学习简史(第一部分):从感知机到BP算法 2016-01-23 机器之心 来自Andrey Kurenkov 作者:Andrey Kurenkov 机器之心编译出品 参与:chen ...
- 【转】Bresenham快速画直线算法
一. 算法原理简介: 算法原理的详细描述及部分实现可参考: http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresen ...
- protocol buffer 整数序列化
http://blog.csdn.net/csfreebird/article/details/7624807 varints用于正整数 (无符号整数) varints 是 一个很不错的技术.将一个整 ...
随机推荐
- P1122 最大子树和
传送门 思路: 任意找一个点为树根.DFS 遍历树,如果子树和为负就直接跳过,不然就统计进答案.( 虽是任意取一点为根,但不一定从这个点出发能够取得最优解,要开一个 ans 记录一下最大值.) 标程: ...
- 714-Card Trick
思维题,把n个卡片倒着放,然后每个卡片循环放到最底下i次,最后出来的结果就是要求的卡牌顺序 #include<stdio.h> #include<string.h> #incl ...
- arch Linux 安装完,无法通过 SSH 远程连接 root 用户问题
访问 arch Linux 主机的该文件 [root@eric-laptop ~]# vim /etc/ssh/sshd_config 对应注释部分后边补上下边三行: LoginGraceTime 1 ...
- 基于Xshell使用密钥方式连接远程主机
基于Xshell使用密钥方式连接远程主机 连接远程主机,就验证身份而言,一般有两种方式,一种是通过用户密码:另一种通过公钥的方式(Public Key). 图1 xshell支持验证登录用户的方式 下 ...
- Andorid Studio中运行模拟器--夜神模拟器
这样可以直接在夜神模拟器上运行app然后在androidstudio上查看log…. 1.下载夜神模拟器 2.修改配置 点击右上角的设置图标,对夜神模拟器的分辨率进行选择,手机版的480×800的就差 ...
- xml字符串,xml对象,数组之间的相互转化
<?phpnamespace Home\Controller;use Think\Controller;class IndexController extends Controller { pu ...
- Angular/Vue调用百度地图+标注点不显示图标+多标注点计算地图中心位置
整理一下~ 一.在vue中调用百度地图 首先当然是申请百度密匙(很简单,不多说) 1.在index.html文件中引入百度地图JavaScript API接口: <script type=& ...
- SQL列转行用逗号隔开
declare @result varchar(255) set @result = ” select @result = @result + cast(F_IT_FWID as varchar( ...
- 依赖注入原理---IoC框架
先来讲一讲,一个简单的依赖注入例子. 1. 依赖 如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖.例如下面类 Human 中用到一个 Fa ...
- Java 代理
代理做一个简单的抽象: 代理模式包含如下角色: Subject:抽象主题角色.可以是接口,也可以是抽象类. RealSubject:真实主题角色.业务逻辑的具体执行者. ProxySubject:代理 ...