大整数乘法中的分治思想(TOOM-COOK的一种使用方法)

2023-11-24 13:30

本文主要是介绍大整数乘法中的分治思想(TOOM-COOK的一种使用方法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

算法分析与设计学习中,接触到一道大整数乘法问题,分享出来,原题目如下:

算法分析在用分治法求两个n位大整数u和v的乘积时,将u和v都分割为长度为n/3的3段。证明可以用5次n/3位整数的乘法求得uv的值。按此思想设计大整数乘积的分治方法,并分析算法的计算复杂性。

先参考一道较为简单的题目:设有两个n位二进制数X,Y,求它们的乘积XY。
分析:按照一般算法,根据小学数学乘法规律,两个数中每位数都需要相应做乘法,则需要的时间复杂度是O(n^2)。

此时,我们考虑将X,Y分为高位、低位,即
X = | A| B | , Y = | C| D|
A,B,C,D均为n/2位,求XY的问题可以转换为:

	X * Y = (A * 2^(n/2) +B )  *(C * 2^(n/2) +D )= AC * 2^n + (AD + BC) * 2^(n/2)+BD			 

则,可以看出利用分治法后,进行了4次n/2位的乘法运算。但是并没有涉及算法性能优化,时间复杂度依然是O(n^2)。

这个算法进行性能提升可以使用如下方法:
先计算

U = (A + B)(C + D), V = AC, W = BD则 Z = XY = V * 2^n +(U - V- W ) * 2^(n/2) + W

上面过程中,由于只使用了U、V、W涉及的3次乘法,比没有优化的少了一种,时间复杂度就降低到了在这里插入图片描述

具体 过程可以参考 Karatsuba算法

Karatsuba算法伪代码实现如下

procedure karatsuba(num1, num2)if (num1 < 10) or (num2 < 10)return num1*num2/* calculates the size of the numbers */m = max(size_base10(num1), size_base10(num2))m2 = m/2/* split the digit sequences about the middle */high1, low1 = split_at(num1, m2)high2, low2 = split_at(num2, m2)/* 3 calls made to numbers approximately half the size */z0 = karatsuba(low1,low2)z1 = karatsuba((low1+high1),(low2+high2))z2 = karatsuba(high1,high2)return (z2*10^(2*m2))+((z1-z2-z0)*10^(m2))+(z0)

具体例子可以参照这个图片
Karatsuba算法

那参照上述步骤,在解决最开始提到的5次n/3位整数乘法中也可以设法将两个大整数U、V分割为

U = |A| B| C
V =|D| E| F

然后利用(A +B +C )(D +E +F)的方法,在分别细分合并其中几项,来简化复杂度,以求达到5次乘法的要求。

PS,这个方法是一开始我的想法,实现后发现只能简化到6次乘法实现,所以最后寻找别的算法。

最后发现有TOOM-COOK方法来做这个工作,而题目涉及的分为3等份的过程就是TOOM-COOK中当n = 3 的特例,也称为TOOM3。

那先来看看TOOM-COOK的一般实现,即不规定分为多少份,设分为m份。

设有两个大整数U,V,利用分治思维将U、V分为如下部分

U  = |U-(m-1)|……|U2 |U1 |U0
V   =|V-(m-1)|……|V2 |V1 |V0
设X = 10^(n/m)

则可以将u和v及其乘积w=uv表示为
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

将U,V和W都看作关于变量X的多项式,可以得到
在这里插入图片描述

此时我们可以取2m-1个不同的数x1,x2,…,x2m-1代入上多项式,可得W(xi) 与 W0,W1,W2……W(2m-2)的关系,转换为矩阵为

在这里插入图片描述
那设B为红框部分的矩阵可以推得
在这里插入图片描述

此时,取x1、 x2、 x3、 x4…. 为不同的数,再结合以下两个式子,
在这里插入图片描述
可以得出W0 、W1、 W2、 W3、 W4与U0、 U1、U2 ,V0、 V1、 V2 的关系
最后移位相加得到最终结果,移位相加可以参考如下的图,因为设的是10进制的数,所以每次移位就是10^(n/m)
在这里插入图片描述

那其实说到这里,相信有人已经明白了TOOM-COOK算法的整体核心思想,整体实现流程可以简单概括为
在这里插入图片描述

那回归到本题,在具体的3等分n位大整数U、V问题中,可以详细描述为以下细节
首先,分治划分U、V

U = |U2 |U1 |U0
V =|V2 |V1 |V0

同样的,设X = 10^(n/3),那U、V及其乘积W = UV可以表示为
在这里插入图片描述
分别取X为5个不同的数,即

X1 = 0,X2 = 1,X3 = -1, X4 = 2,X5 =-2

代入多项式中
在这里插入图片描述
第一个结论,是W(Xi)与U0,U1,U2,U3,V0,V1,V2的关系,暂且将5种X取值下的W(Xi)标记为a,b,c,d,e
在这里插入图片描述
第二个结论,是W(Xi)即a,b,c,d,e与W = UV中各项参数W0,W1,W2,W3,W4的,其中可以看到矩阵B(TOOM-COOK一般方法中提到)
在这里插入图片描述
至此,建立了W0,W1,W2,W3,W4与U1、U2 ,V0、 V1、 V2 的关系,经过运算后,可以求出两个式子

在这里插入图片描述
在这里插入图片描述
再带入W=UV=W0+W1X+W2X2 +W3X3+W4X4 中,可以求出W
到了这一步,相信大家都已经对这道题有了更深的理解,下面我们来看一道具体的应用。

123456*987654

按照程序实现流程,划分UV后,分别求得a,b,c,d,e以及W0,W1,W2,W3,W4如下
在这里插入图片描述
在这里插入图片描述

UV=W0+W1X+W2X2+W3X3+W4X4即W0, W1 , W2, W3, W4移位相加可得W结果
在这里插入图片描述
对比运算,可知结果无误
在这里插入图片描述

最后再说一下时间复杂度,a,b,c,d,e所涉及的一共5次乘法,加减不计,所以得到的递归方程如下:
在这里插入图片描述
求解得复杂度为在这里插入图片描述

TOOM-COOK实现思路和算法可以参考大数乘法问题

/*** Java8中的 Toom-Cook multiplication 3路乘法*/
private static BigInteger multiplyToomCook3(BigInteger a, BigInteger b) {int alen = a.mag.length;int blen = b.mag.length;int largest = Math.max(alen, blen);// k is the size (in ints) of the lower-order slices.int k = (largest+2)/3;   // Equal to ceil(largest/3)// r is the size (in ints) of the highest-order slice.int r = largest - 2*k;// Obtain slices of the numbers. a2 and b2 are the most significant// bits of the numbers a and b, and a0 and b0 the least significant.BigInteger a0, a1, a2, b0, b1, b2;a2 = a.getToomSlice(k, r, 0, largest);a1 = a.getToomSlice(k, r, 1, largest);a0 = a.getToomSlice(k, r, 2, largest);b2 = b.getToomSlice(k, r, 0, largest);b1 = b.getToomSlice(k, r, 1, largest);b0 = b.getToomSlice(k, r, 2, largest);BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;v0 = a0.multiply(b0);da1 = a2.add(a0);db1 = b2.add(b0);vm1 = da1.subtract(a1).multiply(db1.subtract(b1));da1 = da1.add(a1);db1 = db1.add(b1);v1 = da1.multiply(db1);v2 = da1.add(a2).shiftLeft(1).subtract(a0).multiply(db1.add(b2).shiftLeft(1).subtract(b0));vinf = a2.multiply(b2);// The algorithm requires two divisions by 2 and one by 3.// All divisions are known to be exact, that is, they do not produce// remainders, and all results are positive.  The divisions by 2 are// implemented as right shifts which are relatively efficient, leaving// only an exact division by 3, which is done by a specialized// linear-time algorithm.t2 = v2.subtract(vm1).exactDivideBy3();tm1 = v1.subtract(vm1).shiftRight(1);t1 = v1.subtract(v0);t2 = t2.subtract(t1).shiftRight(1);t1 = t1.subtract(tm1).subtract(vinf);t2 = t2.subtract(vinf.shiftLeft(1));tm1 = tm1.subtract(t2);// Number of bits to shift left.int ss = k*32;BigInteger result = vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);if (a.signum != b.signum) {return result.negate();} else {return result;}
}

参考资料
大数乘法问题
Karatsuba
两个n位大整数相乘算法
算法分析设计习题2-5
TOOM-COOK算法
TOOM-COOK
TOOM-3

这篇关于大整数乘法中的分治思想(TOOM-COOK的一种使用方法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/422021

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程