python算法练习——分治法与大整数Karatsuba乘法算法

分治法与大整数Karatsuba乘法算法

分治法是算法设计的常用思想之一,也是Karatsuba乘法算法的基础,笔者练习用python编程语言进行实现。

分治法

分治法(divide and conquer),其设计思想是将无法着手解决的大问题分解成一系列规模较小的相同问题,然后逐个解决小问题。分治法产生的子问题与原始问题相同,只是规模减小,反复使用分治方法,可以使得子问题得规模不断减小,直到能够被直接求解为止。
分治法是算法设计中的一个古老策略,在很多问题中得到了广泛的应用,比如最大、最小问题,矩阵乘法、大整数乘法以及排序。除此之外,这个技巧也是许多高效算法的基础,比如快速傅里叶变换算法和Karatsuba算法。
应用分治法,一般出于两个目的:一是通过分解问题,使无法着手解决的大问题变成容易解决的小问题;二是通过减小问题的规模,降低解决问题的复杂度。
分治法通常使用递归的方式对问题逐级分解,在每个子问题的层面上,分治法基本上可以归纳为以下三个步骤:
(1)分解:将问题分解为若干个规模较小,相互独立且与原问题形式相同的子问题,确保各个子问题的解具有相同的子结构。
(2)解决:如果上一步分解得到的子问题可以解决,则解决这些子问题,否则,对每个子问题使用和上一步相同的方法再次分解,然后求解分解后的子问题,这个过程可能是一个递归的过程。
(3)合并:将上一步解决的各个子问题的解通过某种规则合并起来,得到原问题的解。
分治法的实现模式可以是递归方式,也可以是非递归方式。
分治法的难点是如何将子问题分解,并且将子问题的解合并出原始问题的解。
递归作为一种算法的实现方式,与分治法的思想十分契合。问题的分解不是一步到位的,需要反复使用分治手段,在多个层次上层层分解,这种分解的方法很自然地导致了递归方式的使用。从算法实现的角度看,分治法得到的子问题和原问题是相同的,当然可以用相同的函数来解决,区别只在于问题的规模和范围不同。通过特定的函数参数安排,使得同一个函数可以解决不同规模的问题,这就是递归方法的基础。

大整数Karatsuba乘法算法

两个n位大整数相乘,普通乘法算法的时间复杂度一般是 O ( n 2 ) O(n^2) O(n2)。但是Anatolii Alexeevitch Karatsuba博士在1960年提出了一种时间复杂度为 O ( 3 n 1.585 ) O(3n^{1.585}) O(3n1.585) 1.585 = l o g 2 3 1.585=log_{2}3 1.585=log23)的快速乘法算法,这就是Karatsuba算法。该算法就是利用了分治法的思想,将 n n n位大整数分解成两个接近 n / 2 n/2 n/2位的大整数,通过3次 n / 2 n/2 n/2位大整数的乘法和少量加法操作,避免了直接进行 n n n位大整数乘法计算,有效地降低了乘法计算的计算量。

计算原理

假设有两个 n n n位的 M M M进制大整数 x x x y y y利用一个小于 n n n的正数 k k k(通常 k k k的取值位 n / 2 n/2 n/2左右),将 x x x y y y分解为两个部分: x = x 1 M k + x 0 x=x_1M^k+x_0 x=x1Mk+x0 y = y 1 m k + y 0 y=y_1m^k+y_0 y=y1mk+y0 x x x y y y的乘积可计算为: x y = ( x 1 M k + x 0 ) ( y 1 m k + y 0 ) = x 1 y 1 M 2 k + ( x 1 y 0 + x 0 y 1 ) M k + x 0 y 0 xy=(x_1M^k+x_0)(y_1m^k+y_0)=x_1y_1M^{2k}+(x_1y_0+x_0y_1)M^{k}+x_0y_0 xy=(x1Mk+x0)(y1mk+y0)=x1y1M2k+(x1y0+x0y1)Mk+x0y0这样就将 x x x y y y的乘法计算转化成四次较小规模的乘法计算和少量的加法运算,其中 M 2 k 和 M^{2k}和 M2k M k M^k Mk的计算都可以通过移位高效地处理。不过上述操作还可以继续优化,我们令 z 0 = x 0 y 0 , z 1 = x 1 y 0 + x 0 y 1 , z 2 = x 1 y 1 z_0=x_0y_0, z_1=x_1y_0+x_0y_1,z_2=x_1y_1 z0=x0y0,z1=x1y0+x0y1,z2=x1y1,则 x y xy xy的乘积可表示为: x y = z 2 M 2 k + z 1 M k + z 0 xy=z_2M^{2k}+z_1M^k+z_0 xy=z2M2k+z1Mk+z0计算 z 1 z_1 z1需要两次乘法,对 z 1 z_1 z1的计算可以优化为: z 1 = ( x 1 + x 0 ) ( y 1 + y 0 ) − x 1 y 1 − x 0 y 0 = ( x 1 + x 0 ) ( y 1 + y 0 ) − z 2 − z 0 z_1=(x_1+x_0)(y_1+y_0)-x_1y_1-x_0y_0=(x_1+x_0)(y_1+y_0)-z_2-z_0 z1=(x1+x0)(y1+y0)x1y1x0y0=(x1+x0)(y1+y0)z2z0由于 z 0 z_0 z0 z 2 z_2 z2都计算过了,因此就只需以此乘法,辅助两次加法和两次减法即可计算出 z 1 z_1 z1

算法实现

本例中, M = 10 M=10 M=10,即大整数是 10 10 10进制的。

def Karatsuba(x, y):
	'''n位10进制大数相乘'''
	'''递归终止条件'''
	if len(str(x))==1 or len(str(y))==1:
		return x*y
	
	n = max(len(str(x)), len(str(y)))
	'''需要注意的是,此处的k必须是整数'''
	k = n//2

	x1 = x // 10**k
	x0 = x % 10**k
	y1 = x // 10**k
	y0 = x % 10**k

	z0 = Karatsuba(x0, y0)
	z2 = Karatsuba(x1, y1)
	z1 = Karatsuba((x1+x0), (y1+y0)) - z2 - z0
	
	xy = z2*10**(k*2) + z1*10**(k) + z0

	return xy

def main():
	option = input('是否选择使用默认数据(Y/N): ')
	if option == 'Y':
		x = 767389078975544323523755
		y = 643289078976565442454599
	else:
		x = input('请输入x:')
		y = input('请输入y:')
	xy = Karatsuba(x, y)
	print(xy)

main()

运行

在fish终端中的运行结果如下:
在这里插入图片描述
ps:fish终端真好用啊哈哈哈哈

致谢

感谢广大网友。
主要参考内容:
[1]《算法的乐趣》——王晓华
[2]https://blog.csdn.net/sunny1235435/article/details/95603969

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值