您的位置:首页 > 其它

POJ1001 Exponentiation解题报告

2018-02-10 15:45 423 查看

POJ1001 Exponentiation解题报告

标签(空格分隔): C++

参考:大数运算(4)——大数乘法

1.题目内容:

Description

Problems involving the computation of exact values of very large magnitude and precision are common. For example, the computation of the national debt is a taxing experience for many computer systems.

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.

Input

The input will consist of a set of pairs of values for R and n. The R value will occupy columns 1 through 6, and the n value will be in columns 8 and 9.

Output

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.

Sample Input

95.123 12

0.4321 20

5.1234 15

6.7592 9

98.999 10

1.0100 12

Sample Output

548815620517731830194541.899025343415715973535967221869852721

.00000005148554641076956121994511276767154838481760200726351203835429763013462401

43992025569.928573701266488041146654993318703707511666295476720493953024

29448126.764121021618164430206909037173276672

90429072743629540498.107596019456651774561044010001

1.126825030131969720661201

2.解题关键:

本题是求幂操作,函数pow即可实现该操作,但需要注意的是这里是小数的幂,没有任何一种数值类型可以直接显示如此长的数值结果

float尾数最大为(2^23)-1=8388607

double尾数最大为(2^52)-1=4503599627370495

远远不能达到题目中结果输出的长度

因此需要自己实现高精度乘法,即该问题是一个大数问题,需要用字符串来表示结果

高精度乘法:

模拟人工计算:从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。

我们以125*53为例来说明计算过程:

1、先算125*3,3*5得到15个1,3*2得到6个10,3*1得到3个100;

下标543210
结果0003615
2、接下来算125*5,5*5得到25个10,2*5得到10个100,5*1得到5个1000;

下标543210
结果005133115
3、乘法过程完毕。接下来从 a[0]开始向高位逐位处理进位问题。a[0]留下5,把1 加到a1上,a1变为32 后,应留下2,把3 加到a[2]上……最终使得a里的每个元素都是1 位数,结果就算出来了

下标543210
结果006625
编程时用到的一个规律:

一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。

即:ans[i+j] = a[i]*b[j];

3.需要注意的几种特殊情况

情况一:0.4321 20

(1)以0开头的小数,这是一种特殊情况

第一次倒置,去掉小数点时若不对尾部的0做特殊,则结果会比原来大10倍

0.4321正常情况去掉小数点倒置应为1234

若不处理则会倒置为12340

因此应注意,如果做大数运算不倒置则不需要注意

L36-L46
L97-L100
处理这种情况


(2)另外这种情况是小于1的小数,最后在补小数点时也是一种特殊情况,移位的位数大于结果位数,需要在小数点后补上一些0,如该结果需要补上7个0:

.00000005148554641076956121994511276767154838481760200726351203835

L251-L255
处理这种情况


情况二:2.0000 2

小数点后全是0

如果不对小数点做处理,则最终输出会带小数点,(如4.)

应去掉小数点当作整数进行正常计算,由程序
L81-L93
处理这种情况


去掉尾部多余的0由程序
L68-L77
完成


情况三:12.345 0

n为0的情况

直接判断n是否为0,是则直接输出1

情况四:20.000 1

小数点后全是0且n为1

若不处理则输出20.000,小数点和尾部多余的0未处理

应判断n等于1后处理掉尾部的0和小数点

4.完整代码:

#include <iostream>
#include <cstring>
using namespace std;

char * S_Result;             //计算结果
char S_BaseNum[1000];        //底数
int n;                       //幂
int Len_Result;              //结果字符串的长度

//高精度乘法(大数乘法)
//将字符串转成整数求乘法,再加入小数点
//按位相乘
char * HighPreMult(char * Par_C_BaseNum_1, char * Par_C_BaseNum_2)
{
int Len_1, Len_2;                       //字符串长度
int Point_Pos_1 = 0;                    //字符串1小数点位置
int Point_Pos_2 = 0;                    //字符串2小数点位置
int Multiplier_1, Multiplier_2;         //按位相乘时的乘子
int Med_Result;                         //按位相乘时的中间结果
int flag1 = 0;                      //是否有小数点的标志1,没有小数点为0,有小数点置1
int flag2 = 0;                      //是否有小数点的标志2,没有小数点为0,有小数点置1
int Flag1 = 0;                  //判断字符串1首位是否为0,默认为0,首位为‘0’则置为1,首位为‘.’则置为2,针对小于1的小数这种特殊情况   如0.4321 20
int Flag2 = 0;                  //判断字符串2首位是否为0,默认为0,首位为‘0’则置为1,首位为‘.’则置为2,针对小于1的小数这种特殊情况   如0.4321 20
Len_1 = strlen(Par_C_BaseNum_1);
Len_2 = strlen(Par_C_BaseNum_2);
char *Reverse_Par_C_BaseNum_1 = new char[(Len_1) * sizeof(char)];          //反向存储的字符串1
char *Reverse_Par_C_BaseNum_2 = new char[(Len_2) * sizeof(char)];          //反向存储的字符串2
int *Reverse_N_Result = new int[(Len_1 + Len_2) * sizeof(int)];            //整数型反向结果
for (int i = 0; i < Len_1 + Len_2 + 1; i++)
{
Reverse_N_Result[i] = 0;
}
char *Out_S_Result = new char[(Len_1 + Len_2) * sizeof(char)];             //输出结果

/********************字符串1的处理********************/
//针对小于1的小数这种特殊情况   如0.4321 20
if (Par_C_BaseNum_1[0] == '0')
{
Flag1 = 1;
Len_1 = Len_1 - 1;
}
//针对小于1的小数这种特殊情况   如0.4321 20
if (Par_C_BaseNum_1[0] == '.')
{
Flag1 = 2;
}

for (int i = 0; i < Len_1; i++)
{
if(Flag1==1)
Reverse_Par_C_BaseNum_1[i] = Par_C_BaseNum_1[Len_1 - i];    //针对小于1的小数这种特殊情况   如0.4321 20
else
Reverse_Par_C_BaseNum_1[i] = Par_C_BaseNum_1[Len_1 - i - 1];  //字符串倒置,尾部变首部,用于高精度乘法

if (Reverse_Par_C_BaseNum_1[i] == '.')
{
flag1 = 1;                          //字符串1有小数点,标志置位
Point_Pos_1 = i;
}
}
Reverse_Par_C_BaseNum_1[Len_1] = NULL;     //去掉Len_1以后多申请的内存的值

//如果字符串1有小数点则去掉小数点
if (flag1 == 1)
{
/************************去除字符串1尾部的0************************/
int N_Del_Zero_Count_1 = 0;                     //尾部0数目计数,用于对小数点位置进行修正
while (Reverse_Par_C_BaseNum_1[0] == '0')
{

N_Del_Zero_Count_1++;
for (int i = 0; i < Len_1; i++)
{
Reverse_Par_C_BaseNum_1[i] = Reverse_Par_C_BaseNum_1[i + 1]; //若第一位为0(此时字符串已经倒置,因此尾部变首部)则向左移位以删除第一个0
}
Len_1--;                                                         //字符串长度实时修正
}
Point_Pos_1=Point_Pos_1 - N_Del_Zero_Count_1;                        //对之前找到的小数点位置进行修正
/************************去除字符串1尾部的0************************/

//如果字符串1最后一位是小数点
if (Reverse_Par_C_BaseNum_1[0] == '.')
{
for (int i = 0; i < Len_1 - 1; i++)
{
Reverse_Par_C_BaseNum_1[i] = Reverse_Par_C_BaseNum_1[i + 1]; //若第一位为0(此时字符串已经倒置,因此尾部变首部)则向左移位以删除第一个0
}
Len_1--;
flag1 = 0;        //把小数点置位标志清除
Flag1 = 0;        //标志位都复原
Point_Pos_1 = 0;  //把小数点位置重置为0
Reverse_Par_C_BaseNum_1[Len_1] = NULL;
}
else
{
//针对小于1的小数这种特殊情况   如0.4321 20
if (Flag1 == 2)
{
Reverse_Par_C_BaseNum_1[Point_Pos_1] = NULL;          //如果第一个是小数点就直接把小数点变为NULL,截断后面的字符
}
else
{
for (int i = Point_Pos_1; i < Len_1 - 1; i++)
{
Reverse_Par_C_BaseNum_1[i] = Reverse_Par_C_BaseNum_1[i + 1];
}
Reverse_Par_C_BaseNum_1[Len_1 - 1] = NULL;
}
}
}
/********************字符串1的处理********************/

/********************字符串2的处理********************/
//针对小于1的小数这种特殊情况   如0.4321 20
if (Par_C_BaseNum_2[0] == '0')
{
Flag2 = 1;
Len_2 = Len_2 - 1;
}
//针对小于1的小数这种特殊情况   如0.4321 20
if (Par_C_BaseNum_2[0] == '.')
{
Flag2 = 2;
}

for (int i = 0; i < Len_2; i++)
{
//针对小于1的小数这种特殊情况   如0.4321 20
if (Flag2 == 1)
{
Reverse_Par_C_BaseNum_2[i] = Par_C_BaseNum_2[Len_2 - i];
}
else
Reverse_Par_C_BaseNum_2[i] = Par_C_BaseNum_2[Len_2 - i - 1];

if (Reverse_Par_C_BaseNum_2[i] == '.')
{
flag2 = 1;                          //字符串2有小数点,标志置位
Point_Pos_2 = i;
}
}
Reverse_Par_C_BaseNum_2[Len_2] = NULL;     //去掉Len_2以后多申请的内存的值

//如果字符串2有小数点则去掉小数点
if (flag2 == 1)
{
/************************去除字符串2尾部的0************************/
int N_Del_Zero_Count_2 = 0;                     //尾部0数目计数,用于对小数点位置进行修正
while (Reverse_Par_C_BaseNum_2[0] == '0')
{
N_Del_Zero_Count_2++;
for (int i = 0; i < Len_2 - 1; i++)
{
Reverse_Par_C_BaseNum_2[i] = Reverse_Par_C_BaseNum_2[i + 1]; //若第一位为0(此时字符串已经倒置,因此尾部变首部)则向左移位以删除第一个0
}
Len_2--;                                                     //字符串长度实时修正
}
Point_Pos_2 = Point_Pos_2 - N_Del_Zero_Count_2;                      //对之前找到的小数点位置进行修正
/************************去除字符串2尾部的0************************/

//如果字符串2最后一位是小数点
if (Reverse_Par_C_BaseNum_2[0] == '.')
{
for (int i = 0; i < Len_2 - 1; i++)
{
Reverse_Par_C_BaseNum_2[i] = Reverse_Par_C_BaseNum_2[i + 1]; //若第一位为0(此时字符串已经倒置,因此尾部变首部)则向左移位以删除第一个0
}
Len_2--;
flag2 = 0;        //把小数点置位标志清除
Flag2 = 0;        //标志位都复原
Point_Pos_2 = 0;  //把小数点位置重置为0
Reverse_Par_C_BaseNum_2[Len_2] = NULL;
}
else
{
//针对小于1的小数这种特殊情况   如0.4321 20
if (Flag2 == 2)
{
Reverse_Par_C_BaseNum_2[Point_Pos_2] = NULL;          //如果第一个是小数点就直接把小数点变为NULL,截断后面的字符
}
else
{
for (int i = Point_Pos_2; i < Len_2 - 1; i++)
{
Reverse_Par_C_BaseNum_2[i] = Reverse_Par_C_BaseNum_2[i + 1];
}
Reverse_Par_C_BaseNum_2[Len_2 - 1] = NULL;
}
}
}
/********************字符串2的处理********************/

//按位相乘
for (int i = 0; i < Len_1 - flag1; i++)
{
for (int j = 0; j < Len_2 - flag2; j++)
{
Multiplier_1 = Reverse_Par_C_BaseNum_1[i] - '0';
Multiplier_2 = Reverse_Par_C_BaseNum_2[j] - '0';
Med_Result = Multiplier_1*Multiplier_2;
Reverse_N_Result[i + j] = Reverse_N_Result[i + j] + Med_Result;
}
}

//进位处理
for (int i = 0; i < Len_1 + Len_2; i++)//进行进位
{
if (Reverse_N_Result[i] >= 10)  //若>=10
{
Reverse_N_Result[i + 1] = Reverse_N_Result[i + 1] + Reverse_N_Result[i] / 10;  //将十位上数字进位
Reverse_N_Result[i] = Reverse_N_Result[i] % 10;                                //将个位上的数字留下
}
}

//预估Len_1 + Len_2,实际肯定没有这么多,因此最后会多出一些0位,要消除
int N_Del_Pos = Len_1 + Len_2;
for (; N_Del_Pos > 0; N_Del_Pos--)  //删除0的前缀
{
if (Reverse_N_Result[N_Del_Pos] == 0)
{
continue;
}
else
{
break;
}
}

int N_Result_Index = 0;                    //计算完成后正序的字符索引
for (; N_Del_Pos >= 0; N_Del_Pos--)
{
Out_S_Result[N_Result_Index] = (char)Reverse_N_Result[N_Del_Pos] + '0';      //再次倒置赋值
N_Result_Index++;
}

//如果有小数点,要在结果加上小数点
if (flag1 == 1 || flag2 == 1)
{
//针对小于1的小数这种特殊情况   如0.4321 20
//小数相乘需要在左边补一些0,补0的数目为Point_Pos_1 + Point_Pos_2-N_Result_Index
//因为有小数点,所以右移的位数应该比补0的数目多1
if (N_Result_Index - Point_Pos_1 - Point_Pos_2 <= 0)
{
for (int i = N_Result_Index - 1; i >= 0; i--)
{
Out_S_Result[i + Point_Pos_1 + Point_Pos_2 - N_Result_Index + 1] = Out_S_Result[i];
}

Out_S_Result[0] = '.';             //在第一位补小数点

//补0
for (int i = 1; i < Point_Pos_1 + Point_Pos_2 - N_Result_Index + 1; i++)
{
Out_S_Result[i] = '0';
}
Out_S_Result[N_Result_Index + Point_Pos_1 + Point_Pos_2 - N_Result_Index + 1] = NULL;    //删除多余部分
}
else
{
for (int i = N_Result_Index - 1; i >= N_Result_Index - Point_Pos_1 - Point_Pos_2 - 1; i--)
{
Out_S_Result[i + 1] = Out_S_Result[i];
}
Out_S_Result[N_Result_Index - Point_Pos_1 - Point_Pos_2] = '.';
Out_S_Result[N_Result_Index + 1] = NULL;                            //删除多余部分
}
}
else
Out_S_Result[N_Result_Index] = NULL;

return Out_S_Result;// Out_S_Result;
}

int main()
{
while (cin >> S_BaseNum >> n)
{
if (n == 0)
{
cout << "1" << endl;
}
if (n == 1)
{
for (int i = strlen(S_BaseNum) - 1; i >= 0; i--)
{
if (S_BaseNum[i] == '0')
{
S_BaseNum[i] = NULL;
}

if (S_BaseNum[i] == '.')
{
S_BaseNum[i] = NULL;
break;
}
}
cout << S_BaseNum << endl;
}
else
{
S_Result = S_BaseNum;
for (int i = 0; i < n - 1; i++)
{
S_Result = HighPreMult(S_Result, S_BaseNum);
}
cout << S_Result << endl;
}
}
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: