基础算法--分治法

大整数乘法

问题描述

通常,在分析一个算法的计算复杂性时,都将加法和乘法运算当作是基本运算来处理,即将执行一次加法或乘法运算所需的计算时间当作一个仅取决于计算机硬件处理速度的常数。

这个假定仅在计算机硬件能对参加运算的整数直接表示和处理时才是合理的。然而,在某些情况下,我们要处理很大的整数,它无法在计算机硬件能直接表示的范围内进行处理。若用浮点数来表示它,则只能近似地表示它的大小,计算结果中的有效数字也受到限制。若要精确地表示大整数并在计算结果中要求精确地得到所有位数上的数字,就必须用软件的方法来实现大整数的算术运算。

请设计一个有效的算法,可以进行两个n位大整数的乘法运算。

参考解答

设X和Y都是n位的二进制整数,现在要计算它们的乘积XY。我们可以用小学所学的方法来设计一个计算乘积XY的算法,但是这样做计算步骤太多,显得效率较低。如果将每2个1位数的乘法或加法看作一步运算,那么这种方法要作O(n2)步运算才能求出乘积XY。下面我们用分治法来设计一个更有效的大整数乘积算法。

图6-3 大整数X和Y的分段

我们将n位的二进制整数X和Y各分为2段,每段的长为n/2位(为简单起见,假设n是2的幂),如图6-3所示。

由此,X=A2n/2+B ,Y=C2n/2+D。这样,X和Y的乘积为:

XY=(A2n/2+B)(C2n/2+D)=AC2n+(AD+CB)2n/2+BD (1)

如果按式(1)计算XY,则我们必须进行4次n/2位整数的乘法(AC,AD,BC和BD),

以及3次不超过n位的整数加法(分别对应于式(1)中的加号),此外还要做2次移位(分别对应于式(1)中乘2n和乘2n/2)。所有这些加法和移位共用O(n)步运算。设T(n)是2个n位整数相乘所需的运算总数,则由式(1),我们有:

(2)

由此可得T(n)=O(n2)。因此,用(1)式来计算X和Y的乘积并不比小学生的方法更有效。要想改进算法的计算复杂性,必须减少乘法次数。为此我们把XY写成另一种形式:

XY=AC2n+[(A-B)(D-C)+AC+BD]2n/2+BD (3)

虽然,式(3)看起来比式(1)复杂些,但它仅需做3次n/2位整数的乘法(AC,BD和(A-B)(D-C)),6次加、减法和2次移位。由此可得:

(4)

解递归方程套用公式法马上可得其解为T(n)=O(nlog3)=O(n1.59)。利用式(3),并考虑到X和Y的符号对结果的影响,我们给出大整数相乘的完整算法MULT如下:

function MULT(X,Y,n); {X和Y为2个小于2n的整数,返回结果为X和Y的乘积XY}

begin

S:=SIGN(X)*SIGN(Y); {S为X和Y的符号乘积}

X:=ABS(X);

Y:=ABS(Y); {X和Y分别取绝对值}

if n=1 then

if (X=1)and(Y=1) then return(S)

else return(0)

else begin

A:=X的左边n/2位;

B:=X的右边n/2位;

C:=Y的左边n/2位;

D:=Y的右边n/2位;

ml:=MULT(A,C,n/2);

m2:=MULT(A-B,D-C,n/2);

m3:=MULT(B,D,n/2);

S:=S*(m1*2n+(m1+m2+m3)*2n/2+m3);

return(S);

end;

end;

#include <iostream> using namespace std; #define MAX 64 struct Number { char num[MAX];//结果 int size; //长度 char sign; //符号 }; //输入函数,初始化num全部为0,不足n位的补齐 void Input(Number &number1,Number &number2) { char num1[MAX], num2[MAX]; int a, b, size, i; cin>>num1>>num2; a = strlen(num1); b = strlen(num2); if (num1[0] == '-') a=a-1; if (num2[0] == '-') b=b-1; size = (a > b) ? a: b; number1.size = size; number2.size = size; if (num1[0] == '-') { number1.sign = '-'; if(a>b) { for ( i=1; i<a+1;i++) number1.num[MAX-size+i-1] = num1[i]; } else { for ( i=1; i<a+1;i++) number1.num[MAX-size+i-1+b-a] = num1[i]; } } else { number1.sign = '+'; if(a>b) { for ( i=0; i<a;i++) number1.num[MAX-size+i] = num1[i]; } else { for ( i=0; i<a;i++) number1.num[MAX-size+i+b-a] = num1[i]; } } if (num2[0] == '-') { number2.sign = '-'; if (a<b) { for ( i=1; i<b+1;i++) number2.num[MAX-size+i-1] = num2[i]; } else { for ( i=1; i<b+1;i++) number2.num[MAX-size+i-1+a-b] = num2[i]; } } else { number2.sign = '+'; if(a<b) { for ( i=0; i<b;i++) number2.num[MAX-size+i] = num2[i]; } else { for ( i=0; i<b;i++) number2.num[MAX-size+i+a-b] = num2[i]; } } } //计算10的n次方 Number Order(int n) { Number num; num.size = n+1; num.sign = '+'; int i; num.num[MAX-num.size]='1'; for (i=1; i<num.size; i++) num.num[MAX-num.size+i]='0'; return num; } //减法 Number Sub(Number number1, Number number2) { Number num,num1; int i,a,b,m,size; for (i=0; i<MAX; i++) num.num[i]='0'; a=number1.size; b=number2.size; size = (a > b) ? a: b; bool flag = false; num.sign='+'; num.size=size; for (i=0; i<size; i++) { if(number1.num[MAX-size+i]!=number2.num[MAX-size+i]) { if (number1.num[MAX-size+i]<number2.num[MAX-size+i]) { flag = true; } break; } } if (flag) { num.sign='-'; num1=number1; number1=number2; number2=num1; } for(i=0; i<size; i++) { m=0; if(number1.num[MAX-1-i]-number2.num[MAX-1-i]<0) { m=10; number1.num[MAX-2-i]=number1.num[MAX-2-i]-1; } num.num[MAX-1-i]=number1.num[MAX-1-i]-number2.num[MAX-1-i]+48+m; } return num; } //加法 Number ToAdd(Number number1, Number number2) { Number num; int i,a,b,m,size; for (i=0; i<MAX; i++) num.num[i]='0'; a=number1.size; b=number2.size; size = (a > b) ? a: b; if (number1.sign-number2.sign!=0) { if(number1.sign=='+'&&number2.sign=='-') num = Sub(number1,number2); if(number1.sign=='-'&&number2.sign=='+') num = Sub(number2,number1); return num; } for(i=0; i<size; i++) { m=0; if(number1.num[MAX-1-i]+number2.num[MAX-1-i]-96>=10) { if(i==size-1) num.num[MAX-2-i]='1'; m=10; number1.num[MAX-2-i]=number1.num[MAX-2-i]+1; } num.num[MAX-1-i]=number1.num[MAX-1-i]+number2.num[MAX-1-i]-48-m; } if(num.num[MAX-size-1]!='0') num.size=size+1; else num.size=size; if (number1.sign=='+'&&number2.sign=='+') num.sign='+'; if (number1.sign=='-'&&number2.sign=='-') num.sign='-'; return num; } Number Add(Number number1, Number number2, Number number3) { Number num1,num; num1=ToAdd(number1,number2); num=ToAdd(num1,number3); return num; } //求数组的左半部分 Number HalfLeft(Number number) { Number num; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number.size/2; i++) num.num[MAX-number.size/2+i]=number.num[MAX-number.size+i]; num.sign = '+'; num.size = number.size/2; return num; } //求数组的右半部分 Number HalfRight(Number number) { Number num; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number.size/2; i++) num.num[MAX-number.size/2+i]=number.num[MAX-number.size/2+i]; num.sign = '+'; num.size = number.size/2; return num; } //乘法 Number Single1Mult(Number number1, Number number2) { Number num; int a,i; char str[3]; for (i=0; i<MAX; i++) num.num[i]='0'; a = (number1.num[MAX-1]-48)*(number2.num[MAX-1]-48); itoa (a,str,10); num.size = strlen(str); for(i=0; i<num.size; i++) num.num[MAX-num.size+i]=str[i]; return num; } Number Single2Mult(Number number1, Number number2) { Number num; num.size = number1.size+number2.size-1; num.sign=number1.sign; int i; for (i=0; i<MAX; i++) num.num[i]='0'; for (i=0; i<number1.size; i++) num.num[MAX-num.size+i]=number1.num[MAX-number1.size+i]; return num; } //结果计算 Number Result(Number M1, Number M2, Number M3, int n) { Number result, p1, p2, p3; p1 = Single2Mult(M1,Order(n)); p2 = Add(M1,M2,M3); p3 = Single2Mult(p2,Order(n/2)); result = Add(p1,p3,M3); return result; } //分治法求解 Number Mult(Number number1, Number number2, int n) { Number result, A, B, C, D, M1, M2, M3; if(n==1) { result = Single1Mult(number1,number2); if(number1.sign-number2.sign==0) result.sign='+'; else result.sign='-'; return result; } else { A = HalfLeft(number1); B = HalfRight(number1); C = HalfLeft(number2); D = HalfRight(number2); M1 = Mult(A,C,n/2); M2 = Mult(Sub(A,B),Sub(D,C),n/2); M3 = Mult(B,D,n/2); result = Result(M1,M2,M3,n); if(number1.sign-number2.sign==0) result.sign='+'; else result.sign='-'; return result; } } //结果优化 void Del(Number& num) { int i,j; j=num.size; for(i=0; i<j; i++) { if(num.num[MAX-j+i]!='0') break; num.size=num.size-1; } } int main() { int i; Number number1, number2, result; while(1) { for (i=0; i<MAX; i++) { number1.num[i]='0'; number2.num[i]='0'; } Input(number1, number2); // result = ToAdd(number1,number2); result = Mult(number1,number2,number1.size); Del(result); cout<<result.sign; for(i=0; i<result.size; i++) cout<<result.num[MAX-result.size+i]; cout<<" 长度: "<<result.size<<endl; } return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值