整数的故事(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 是 一个很不错的技术.将一个整 ...
随机推荐
- JS中for in 与 for of
// 数组var A=[4,6,74,67]; for in:拿到的是数组下标 for (let i in A){ console.log(i); } //0,1,2,3 for of:拿到的是数组元 ...
- Django Context对象 + 过滤器 + 标签
玩一玩上下文(context)对象 多数时间,你可以通过传递一个完全填充(full populated)的字典给 Context() 来初始化 上下文(Context) . 但是初始化以后,你也可以从 ...
- Redis入门指南之一(简介)
1. 简介 Redis是一个开源的.高性能的.基于键值对的缓存与存储系统,通过提供多种键值数据类型来适应不同的场景下的缓存与存储需求.同时Redis的诸多高级功能使其可以胜任消息队列.任务队列等不同的 ...
- 『OpenCV3』滤波器实现及使用滤波器降噪
一.滤波器实现 我们实现这样一个基于拉普拉斯算子的滤波器核心,并使用它进行滤波,这可以做到锐化图像的效果, 0 -1 0 -1 5 -1 0 -1 0 首先我们完全手动的进行滤波,依赖指针操作, vo ...
- tomcat服务器-谷歌等浏览器-加载本地图片等资源-报not allowed to load local resource的错误解决办法。
把文件存放路径目录映射到tomcat中,方法如下: 1.找到tomcat的配置文件(\conf\server.xml)并打开 2.在[host]与[/host]之间加入如下代码:[<Contex ...
- css3实现自适应的3行,左右行固定宽度,中间自适应,要求先渲染中间部分
https://blog.csdn.net/thqy39/article/details/73512478 https://www.cnblogs.com/ranzige/p/4097453.html ...
- angular学习2
1.为了在angular里面使用bootstrap,可以如下操作 (1)停止正在运行的终端指令:ctrl+c (2)在终端里面输入:npm install bootstrap --save (3)在V ...
- spoj Minimax Triangulation
题解: dp+计算几何 F[i][j]表示第i-j条边的答案 然后转移一下 代码: #include<bits/stdc++.h> using namespace std; ]; ][]; ...
- EasyExcel导入工具(SpringMVC下使用)
easyExcel:由阿里巴巴公司开发,由github托管 github上有详细使用文档 github地址:https://github.com/alibaba/easyexcel/blob/mast ...
- librdkafka安装和php扩展php-rdkafka安装
1.安装librdkafka mac下 brew install librdkafka linux下 git clone https://github.com/edenhill/librdkafk ...
