这算是分治的运用,在java中有个大数类,用着非常方便,但是在c/c++中就没有那么容易了,
一个整数最多也才2^64两个数字的加法或减法我们可以用数组模拟运算一遍,轻松搞定,但是两个数字的乘法呢,
比如给你这样两个数字:
A= 1234567898765432145673;
B=23456789463784628596936285;
现要求求出c=A*B, 好了问题来了怎么用算法实现呢,数组模拟运算效率太低了,一个n位和m位的数字相乘最少进行n*m次乘法运算,
在说如何运算前,我先说说如何减少乘法运算次数:
数学家高斯曾经说 (a1+b1*i)*(a2+b2*i)这样的两个复数,他可以用三次乘法运算计算出结果,
(a1+b1*i)*(a2+b2*i)=a1*b2 + (a1*b2 + b1*a2)*i - b1*b2 ,仔细一看这不是有4次乘法运算么,
其实他用了一个小优化,假设不是复数 (a1+b1)*(a2+b2) = a1*b2 + (a1*b2 + b1*a2)+ b1*b2
那么 (a1*b2 + b1*a2) = (a1+b1)*(a2+b2) - a1*b2 - b1*b2 ,
所以就可以(a1+b1*i)*(a2+b2*i) = a1*b2 + ((a1+b1)*(a2+b2) - a1*b2 - b1*b2) *i -b1*b2 ;三次乘法,got;
说了这么多这对我们做大数的运算有什么作用呢:
是这样的对于两个大数相乘我们肯定是采用分治的方法
数字A= A1 * 10^(n/2) + A2;
B=B1 * 10^(n/2) + B2; (当两个数字不一样长时在断的前面补0)
==>> A*B = A1 * B1 * 10^n +( A1* B2 + A2*B1 ) * 10^(n/2) + A2*B2
分治的思想是:
getans(A, B)
if ( A.length == 1 and B.length == 1 )
return A*B;
else
return getans(A1, B1)*10^n + (getans(A1, B1) + getans(A1, B2) )* 10^(n/2) + getans(B1, B2);
看样子并不难的样子但是我么来算算时间复杂度,还是以乘法作为基本操作(以加法作为基本操作求出结果和乘法一样):
O(1) = 1;
O(n) = 4O(n/2);
推到过程就不写了,但是最后算出来平均时间复杂度是O(n^2),和直接用数组模拟运算的复杂度是一样的,花这么大的力气写个66的代码并不6,
所以就优化,就用高斯的办法来优化,虽然加法次数增加了,但是最终结果是怎样的呢,推到一地啊就知道了:
换一个姿势后就有了:
数字A= A1 * 10^(n/2) + A2;
B=B1 * 10^(n/2) + B2; (当两个数字不一样长时在断的前面补0)
==>> A*B = A1 * B1 * 10^n +( A*B - A1 * B1 * 10^n - A2*B2 ) * 10^(n/2) + A2*B2;
代码就变成下面的样子:
getans(A, B)
if ( A.length == 1 and B.length == 1 )
return A*B;
else
X <— getans(A1, B1)*10^n ;
Y <— getans(A2, B2) ;
return X + ( getans(A,B) - X - Y )*10^(n/2) + Y;
在来算时间复杂度就变成了:
O(1) = 1;
O(n) = 3*O(n/2);
最后平均时间复杂度就变成了O(n * log2,3 )了这不就小于O(n^2) 了,
(平均时间复杂度的推导过程涉及到数学变换,所以没有写,这一个递推式的求法很多书山也有讲)
关于具体的代码,细节还是较多,等有空打完了在贴,欢迎大神指正