泪眼成诗

导航

POJ1001

其实此题就是大数相乘的例子,只是输出的要求很苛刻.

题目:This problem requires that you write a program to compute the exact value of Rn where R is a real number ( 0.0 < R < 99.999 ) and n is an integer such that 0 < n <= 25.

输出要求:

The output will consist of one line for each line of input giving the exact value of R^n. Leading zeros should be suppressed in the output. Insignificant trailing zeros must not be printed. Don't print the decimal point if the result is an integer.
代码:
代码
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4
5  int GetPointPos(const char *,int len);//获取小数点的位置
6  void Multiply(const int *data,int *ans,int ca,int cb);
7 void Show(const int *ans,int len,int pos);
8 int main(void)
9 {
10 char num[20];
11 int n;
12 int len,l;
13 int pos = 0;//the position of point
14 int i,j;
15 int *arr;
16 int point;
17 int ans[200];
18
19 while(scanf("%s %d",num,&n) == 2)
20 {
21 l = strlen(num);
22 pos = GetPointPos(num,l);
23
24 point = pos * n;
25
26 if(pos)
27 len = l-1;
28 else
29 len = l;
30
31 arr = (int*)malloc(sizeof(int)*len);
32
33 for(i = l-1,j=0; i>=0; i--)//这部分是将字符串的数字转换为整型的数组存储
34 {
35 if(num[i] == '.')
36 continue;
37 arr[j++] = (int)(num[i]-48);//(num[i]-'0')
38
39 }
40
41 for(i=0;i<len;i++)//初始化结果值,由于结果第一次相乘也要被用作乘数,所以要将其初始化为输入数据
42 ans[i] = arr[i];
43 for(i=len;i<200;i++)
44 ans[i]=0;
45
46 if(n>1)
47 {
48 for(j = 0; j < n-1; j++)
49 {
50 Multiply(arr,ans,len,(j+1)*len);
51
52 }
53 Show(ans,n*len,point);
54
55 }
56 else if(n == 1)
57 {
58 Show(ans,len,pos);
59 }
60 else
61 printf("1");
62
63 putchar('\n');
64 free(arr);
65
66
67 }
68
69 return 0;
70 }
71
72 int GetPointPos(const char *str,int len)
73 {
74 int pos = 0;
75 int i;
76
77 for(i = len-1; i>=0; i--)
78 {
79 if(str[i] == '.')
80 {
81 return pos;
82 }
83 pos++;
84 }
85 return 0;
86 }
87 void Multiply(const int *data,int *ans,int ca,int cb)
88 {
89 int i,j;
90
91 int *s = (int*)malloc(sizeof(int)*(ca+cb));//两数相乘,结果的位数不大于两个数的位数之和
92
93 for(i=0; i<(ca+cb); i++)
94 s[i] = 0;
95
96 for(i=0; i<ca; i++)//核心
97 for(j = 0;j<cb; j++)
98 {
99 s[i+j] += data[i] * ans[j];
100 }
101 for(i=0;i<(ca+cb-1);i++)//进位处理
102 {
103 if(s[i]>=10)
104 {
105 s[i+1] += s[i]/10;
106 s[i] %= 10;
107 }
108 }
109 j = ca+cb-1;
110
111 while(s[j] == 0)
112 j--;
113
114 for(i=j;i>=0;i--)
115 ans[i] = s[i];
116
117 free(s);
118
119 }
120
121 void Show(const int *ans,int len,int pos)
122 {
123 int i;
124 int head = 0;
125 int rear =len-1;
126
127 if(pos == 0)//如果输入时整数,则只需将前面的多余0去掉
128 {
129
130 while(ans[rear]==0)
131 rear--;
132 if(rear < 0)//此处为处理输入为00000的情况
133 {
134 printf("0");
135 return ;
136 }
137
138 for(i = rear; i>=head; i--)
139 printf("%d",ans[i]);
140 }
141 else
142 {
143
144 while(ans[head] == 0 && head <pos)
145 head++;
146 while(ans[rear] == 0 && rear >= pos)
147 rear--;
148
149 for(i = rear; i>=head; i--)
150 {
151 if(i == pos-1)
152 printf(".");
153 printf("%d",ans[i]);
154 }
155 }
156
157 }
以下是用分治的方法处理大数问题:

参考解答

设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;

上述二进制大整数乘法同样可应用于十进制大整数的乘法以提高乘法的效率减少乘法次数。下面的例子演示了算法的计算过程。

设X=314l,Y=5327,用上述算法计算XY的计算过程可列表如下,其中带'号的数值是在计算完成AC,BD,和(A-B)(D-C)之后才填入的。


X=3141        A=31       B=41        A-B=-10

Y=5327        C=53       D=27        D-C=-26

           AC=(1643)'

           BD=(1107)'

          (A-B)(D-C)=(260)'

XY=(1643)'104+[(1643)'+(260)'+(1107)']102+(1107)'

  =(16732107)'


A=31        A1=3       B1=1        A1-B1=2

C=53        C1=5       D1=3        D1-C1=-2

           A1C1=15     B1D1=3     (A1-B1)(D1-C1)=-4

AC=1500+(15+3-4)10+3=1643


B=41        A2=4       B2=1        A2-B2=3

D=27        C2=2       D2=7        D2-C2=5

           A2C2=8     B2D2=7     (A2-B2)(D2-C2)=15

BD=800+(8+7+15)10+7=1107


|A-B|=10        A3=1       B3=0        A3-B3=1

|D-C|=26        C3=2       D3=6        D3-C3=4

           A3C3=2     B3D3=0     (A3-B3)(D3-C3)=4

(A-B)(D-C)=200+(2+0+4)10+0=260


如果将一个大整数分成3段或4段做乘法,计算复杂性会发生会么变化呢?是否优于分成2段做的乘法?这个问题请大家自己考虑。

链接:http://www.ahhf45.com/info/Data_Structures_and_Algorithms/problems/problem_set/bignumber_mul/solution.htms

 

posted on 2010-08-17 18:53  泪眼成诗  阅读(1132)  评论(0)    收藏  举报