POJ1001 Exponentiation解题报告

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-L46L97-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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值