zoukankan      html  css  js  c++  java
  • 大整数相加和大整数相乘

    大数问题是指操作数超过了计算机常用数据类型的存储范围,常常是用字符串来模仿整数相加和相乘运算来实现的,在模拟的过程中要注意考虑进位和边界条件。

    1、大整数相加

    先看一下加法的计算过程,如456+56789

       456

    56789

    ---------

    57245

         计算过程是从低位往高位开始计算,计算过程要加上进位,如,计算到5+8的时候要加上前面的进位1,得到14,然后14对10取余作为对应结果的第2位,进位为14对10取正,这样一直计算,直到有一个字符串结束,然后考虑进位和没计算完的另一个字符串相加。

    边界条件:

        两个大整数相加,结果的长度可能与两个数中长度较大的一个相等,也可能比其大1(进位造成),如123+12=135,123长度为3,12长度为2,结果长度为3,再如99+1=100,结果长度为3比99的长度还大1。

        考虑到这样的边界条件,在申请内存的时候需要对结果至少申请长度较大的那个还要大1。

    代码如下:

    #include<iostream>
    #include<string>
    using namespace std;
    //字符串倒置
    void reverse(char *str)
    {
        int len=strlen(str);
        for(int i=0;i<len/2;i++)
        {
            char temp=str[i];
            str[i]=str[len-i-1];
            str[len-i-1]=temp;
        }
    }
    //大数求和
    void bignumsum(char * ope1,char * ope2,char * result)
    {
        reverse(ope1);
        reverse(ope2);
        int len1=strlen(ope1);
        int len2=strlen(ope2);
        int max=len1;
        if(max<len2)
        {
            max=len2;
        }
        memset(result,'0',max+1);
        result[max+1]='';
        int acc=0;
        int i=0;
        while(i<len1 && i<len2)
        {
            int temp=ope1[i]-'0'+ope2[i]-'0'+acc;
            acc=temp/10;
            result[i]=temp%10+'0';
            i++;
        }
        if(i<len1)
        {
            while(i<len1)
            {
                int temp=ope1[i]-'0'+acc;
                acc=temp/10;
                result[i]=temp%10+'0';
                i++;
            }
        }
        if(i<len2)
        {
            while(i<len2)
            {
                int temp=ope2[i]-'0'+acc;
                acc=temp/10;
                result[i]=temp%10+'0';
                i++;
            }
        }
        //考虑到进位,所以result的长度有可能比ope1和ope2中最大的数据长度还多出一位
        if(acc)
        {
            result[i]=acc+'0';
        }
        else
        {
            result[i]='';
        }
        reverse(result);
    }
    int main()
    {
        char num1[4]="456";
        char num2[5]="5678";
        char num3[6];
        bignumsum(num1,num2,num3);
        cout<<num3<<endl;
    
        char num4[4]="456";
        char num5[5]="678";
        char num6[6];
        bignumsum(num4,num5,num6);
        cout<<num6<<endl;
    
        char num7[4]="111";
        char num8[5]="2";
        char num9[6];
        bignumsum(num7,num8,num9);
        cout<<num9<<endl;
        return 0;
    }

    2、大整数相乘

    乘法相对于加法稍微复杂一点,需要同时考虑乘法进位和加法进位,还要注意一下计算过程和结果中的对应关系。

    如计算123*45

    123

      45

    ------

     615

    492

    -------

    5535

        计算过程是从低位往高位计算,第2个操作数中每一位都与第一个操作数中的所有的位计算一次,而每这样计算一次都进行一次结果的更新,结果先被初始化成全0。而计算过程和结果的规律是,每次计算的时候影响的结果位数是两个操作数位数的和,如上述例子中1是123中的第2位(从低位算起,个位按0位来算),4是45中的第1位,那么这两个数的计算过程将会产生影响的是结果中的第3位,计算过程是1*4+0(上一次乘法的进位)=4,4%10=4,这样就确定了492位置上的那个4,然后再利用加法进位和上一轮的结果来更新结果,结果为0(原来结果对应该位的值)+4(此轮乘法计算之后该位置上对应的值)+1(上一轮加法的进位值)=5

       边界条件:

       两个大整数相乘结果的长度最大为两个操作数长度之和,所以申请内存的时候要注意至少申请两个操作数长度之和的内存。

       代码如下:

    #include<iostream>
    #include<string>
    using namespace std;
    
    void reverse(char *str)
    {
        int len=strlen(str);
        for(int i=0;i<len/2;i++)
        {
            char temp=str[i];
            str[i]=str[len-i-1];
            str[len-i-1]=temp;
        }
    }
    void bignummultiply(char *ope1,char *ope2,char *result)
    {
        reverse(ope1);
        reverse(ope2);
        int len1=strlen(ope1);
        int len2=strlen(ope2);
        memset(result,'0',len1+len2);//因为len1和len2两个长度的整数相乘结果最大为len1+len2
        result[len1+len2]='';
        int acc=0;//加法进位
        int mcc=0;//乘法进位
        for(int i=0;i<len2;i++)
        {
            acc=0;
            mcc=0;
            for(int j=0;j<len1;j++)
            {
                int temp1=(ope1[j]-'0')*(ope2[i]-'0')+mcc;
                mcc=temp1/10;
                temp1=temp1%10;
                int temp2=result[i+j]-'0'+temp1+acc;
                acc=temp2/10;
                result[i+j]=temp2%10+'0';
            }
            result[i+len1]=acc+mcc+'0';
        }
        //这里有一个去除后面0的程序,如78900将变成789,这样倒过来之后就会使987,而不是00987
        int k=len1+len2-1;
        while('0'==result[k] && k>=0)
        {
            result[k--]='';
        }
        //最后经result倒置回来得到最终结果
        reverse(result);
    }
    int main()
    {
        char n1[5]="12";
        char n2[5]="2";
        char n3[10];
        bignummultiply(n1,n2,n3);
        cout<<n3<<endl;
    
        char n4[5]="9";
        char n5[5]="9";
        char n6[10];
        bignummultiply(n4,n5,n6);
        cout<<n6<<endl;
    
        char n7[5]="888";
        char n8[5]="66";
        char n9[10];
        bignummultiply(n7,n8,n9);
        cout<<n9<<endl;
        return 0;
    }

        以上加法和乘法的计算过程都先使用reverse将字符串倒置,然后再将结果倒置回来计算的,这样是为了更直观的计算,但是,这样会使程序运行效率稍低。实际可以不用倒置,而靠逻辑去写。

  • 相关阅读:
    在github上面查到了,为什么需要这个padding
    Java 8 新语法习惯,新的函数特性和语法
    数据库连接池配置,获取连接的超时
    用函数式的方式思考,通常以命令式的方式
    centos7 yum方式安装,centos自带mariadb
    mac屏幕脏了怎么办?避免使用粗糙的布
    用 Arthas “庖丁解牛,强大的 Arthas法师来 carry
    图解Knative核心组件,Serving自动伸缩
    vim配置文件
    20200717模拟赛3题解
  • 原文地址:https://www.cnblogs.com/bewolf/p/5055485.html
Copyright © 2011-2022 走看看