zoukankan      html  css  js  c++  java
  • 大数问题(高精度运算)

    一、基本概念

      在某些情况下,我们必须处理相当大的一个整数,运用类型int、long int、long long int 类型均无法对其进行存储。要解决这样的问题,我们就需要自己编写相应的处理程序。在处理大数的时候,可以将其作为字符串读入,然后一个数字一个数字的存储到数组中,然后编写相应运算操作的处理函数即可解决大数问题。

      也就是说在对大数进行运算之前,要先解决对大数进行存储的问题。而这里一一般情况为例,对输入和输入函数进行定义。

    输入处理:

      输入字符串:

    c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
    '1' '2' '3' '3' '4' '5' '6' x x x

      转换为对应数字存储:

    ans[0] ans[1] ans[2] ans[3] ans[4] ans[5] ans[6] ans[7] ans[8] ans[9]
    6 5 4 3 3 2 1 0 0 0

      反向存储好处:

          对大数进行存储以后,是为了要对其进行运算处理。而后的运算有可能会增加数字的位数,如果正向存储的话在出现有进位的情况时则需要将数组整体进行右移然后加入新产生的进位。如果对对大数进行方向存储的话,在运算过程中产生的进位可以直接加到足够大的数组后边,而只需改变数组长度即可,无需进行移位操作,这样一来就方便多了,运算处理完后只需要反向输出结果即可。

    #include<stdio.h>
    #include<string.h> #define LEN 500 /* 输入函数 参数: ans 输入的大数组 len 带回大数的位数,也就是数组的长度 返回: 是否输入成功,0:否,1:是 函数说明: 1、按字符串形式输入大数 2、反向单个数字存入到ans数组中 */ int Input(int* ans,int &len) {
      char c[LEN];
      int i,j;
      if(scanf("%s",&c)<0)return 0;
      len = strlen(c);
      for( i=len-1,j=0; i>=0; i--,j++)
      {
        ans[j] = c[i];
      }
      
      return 1 }
    /* 输入函数 参数: ans 需要输出的大数组 len 数组的长度 函数说明: 反向输入大数数组 */ void Output(int* ans,int &len) {   int i;
      for( i=len-1; i>=0; i--)
        printf("%d",ans[i]);
      printf(" "); }
    int main() { int ans[LEN],len;
      memset(ans,0,sizeof(ans)); Input(ans,len); Output(ans);
    return 0; }

     二、相关例子

      以下是一些有关大数处理的例子,题目来自于南洋理工学院的ACM评测系统,均已通过测评。

    2.1 大整数相加

    /*
    描述
    I have a very simple problem for you. Given two integers A and B, your job is to calculate the Sum of A + B.
    A,B must be positive.
    
    输入
    The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line consists of two positive integers, A and B. Notice that the integers are very large, that means you should not process them by using 32-bit integer. You may assume the length of each integer will not exceed 1000.
    输出 For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line is the an equation "A + B = Sum", Sum means the result of A + B. Note there are some spaces int the equation.
    样例输入 2 1 2 112233445566778899 998877665544332211
    样例输出 Case 1: 1 + 2 = 3 Case 2: 112233445566778899 + 998877665544332211 = 1111111111111111110
    */ #include<stdio.h> #include<string.h> #define LEN 1000+10 /* 输入处理函数 参数: char* in : 未经处理的字符串数组 char* out : 经过处理存储后的数组 返回: 返回out数组长度 */ int Change(char* in,char* out) { int i,j,len; memset(out,0,LEN); len = strlen(in); for( i=len-1,j=0; i>=0; j++,i--) { out[j] = in[i]-'0'; } return len; } /* 输出函数 参数: char* ans1 : 加数 char* ans2 : 加数 char* result : ans1 + ans2 的结果 int len1 : ans1的长度 int len2 : ans2的长度 int len3 : result的长度 */ void Output(char* ans1,char* ans2,char* result,int len1,int len2,int len3) { int i; for( i=len1-1; i>=0; i--) printf("%d",ans1[i]); printf(" + "); for( i=len2-1; i>=0; i--) printf("%d",ans2[i]); printf(" = "); for( i=len3-1; i>=0; i--) printf("%d",result[i]); putchar(' '); } /* 加法操作函数 参数: char* ans1 : 加数 char* ans2 : 加数 char* result : ans1 + ans2 的结果 int len1 : ans1的长度 int len2 : ans2的长度 int len3 : 带回result的长度 */ void Add(char* ans1,char* ans2,char* result,int len1,int len2,int &len3) { int i,len; memset(result,0,LEN); len = len1>len2?len1:len2; for(i=0; i<len; i++) { result[i] += ans1[i] + ans2[i]; if( result[i]>=10)//参数进位 { result[i+1] += result[i]/10; result[i] = result[i]%10; } } if(result[len]!=0) len++; len3 = len; } int main() { //输入输出重定向 //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); char in1[LEN],in2[LEN],ans1[LEN],ans2[LEN],result[LEN]; int len1,len2,len3,n,i; scanf("%d",&n); i=1; while(n--) { scanf("%s%s",&in1,&in2); len1 = Change(in1,ans1); len2 = Change(in2,ans2); Add(ans1,ans2,result,len1,len2,len3); printf("Case %d: ",i++); Output(ans1,ans2,result,len1,len2,len3); } return 1; }

    2.2 大整数相乘

    /*
    POJ 2389
    
    Description
    
    Bulls are so much better at math than the cows. They can multiply huge integers together and get perfectly precise answers ... or so they say. Farmer John wonders if their answers are correct. Help him check the bulls' answers. Read in two positive integers (no more than 40 digits each) and compute their product. Output it as a normal number (with no extra leading zeros). 
    
    FJ asks that you do this yourself; don't use a special library function for the multiplication.
    Input
    
    * Lines 1..2: Each line contains a single decimal number.
    Output
    
    * Line 1: The exact product of the two input lines
    Sample Input
    
    11111111111111
    1111111111
    Sample Output
    
    12345679011110987654321
    
    
    */
    
    #include<stdio.h>
    #include<string.h>
    
    #define LEN 200+10
    
    /*
        输入处理函数
    
        参数:
            int* ans : 经过处理后的反序大数数组
        返回:
            ans的长度,既大数的位数
    */
    int Input(int* ans)
    {
        char in[LEN];
        int i,j,len;
        scanf("%s",&in);
        len = strlen(in);
        for( i=len-1,j=0; i>=0; i--,j++)
        {
            ans[j] = in[i]-'0';
        }
    
        return len;
    }
    
    /*
        输出处理函数:
        
        参数:
            int* ans : 需要输出的大数数组
    */
    void Output(int* ans)
    {
        int i;
        for(i=LEN*2; ans[i]==0 && i>=0; i--);
        if(i<0)
        {
            printf("0
    ");
            return;
        }
        for(; i>=0; i--)
            printf("%d",ans[i]);
        printf("
    ");
    }
    
    /*
        乘法操作函数
    
        参数:
            int* ans1 : 乘数
            int* ans2 : 乘数
            int* result : ans1 * ans2 的结果
            int len1 : ans1的长度
            int len2 : ans2的长度
    */
    void Mul(int* ans1,int* ans2,int* result,int len1,int len2)
    {
        int i,j;
    
        for( i=0; i<len1; i++ )
        {
            for( j=0; j<len2; j++)
            {
                result[i+j] += ans1[i]*ans2[j];
            }
        }
        for(i=0; i<2*LEN-1; i++)//处理进位
        {
            if(result[i]>=10)
            {
                result[i+1] += result[i]/10;
                result[i] = result[i] % 10;
            }
        }
    }
    int main()
    {
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        int ans1[LEN]={0},ans2[LEN]={0},result[2*LEN]={0},len1,len2;
        len1 = Input(ans1);
        len2 = Input(ans2);
        Mul(ans1,ans2,result,len1,len2);
        Output(result);
    
        return 1;
    }

    2.3 大整数相除

    /*
    
    南阳:题目803
    
    描述
     做了A+B Problem,A/B Problem不是什么问题了吧!
    
    输入
    每组测试样例一行,首先一个号码A,中间一个或多个空格,然后一个符号( / 或者 % ),然后又是空格,后面又是一个号码B,A可能会很长,B是一个int范围的数。
    输出
    输出结果。
    样例输入
    110 / 100
    99 % 10
    2147483647 / 2147483647
    2147483646 % 2147483647
    样例输出
    1
    9
    1
    2147483646
    
    解题思路:
    基本的思想是反复做减法,看看从被除数里最多能减去多少个除数,商就是多少。一个一个减显然太慢,如何减得更快一些呢?以7546 除以23 为例来看一下:开始商为0。先减去23 的100 倍,就是2300,发现够减3 次,余下646。于是商的值就增加300。然后用646 减去230,发现够减2 次,余下186,于是商的值增加20。最后用186 减去23,够减8 次,因此最终商就是328。
     所以本题的核心是要写一个大整数的减法函数,然后反复调用该函数进行减法操作。 计算除数的10 倍、100 倍的时候,不用做乘法,直接在除数后面补0 即可。
    
    */
    #include<stdio.h>
    #include<string.h>
    
    #define LEN 1000
    /*
        输入处理函数
    
        参数:
            int* divisor : 被除数
            int* dividend : 除数
            char &c : 带会操作符
            int &dslen : 被除数长度
            int &delen : 除数长度
        返回:
            是否输入结束,0:是,1:否
    */
    int Input(int* divisor,int* dividend,char &c,int &dslen,int &delen)
    {
        char in1[LEN],in2[2],in3[LEN];
        int i,j,k;
        if (scanf("%s%s%s",&in1,&in2,&in3)==EOF)return 0;
    
        c = in2[0];
        dslen = strlen(in1);
        delen = strlen(in3);
        
        for( k=0; k<dslen-1 && in1[k]=='0'; k++);//处理前导数0,最后一个数不用检查,因为最后一个数无论是0还是非零都需要存储
        for( j=0,i=dslen-1; i>=k; j++,i--)
            divisor[j] = in1[i] - '0';
        dslen -= k;
        for( k=0; k<delen-1 && in3[k]=='0'; k++);////处理前导数0,最后一个数不用检查
        for( j=0,i=delen-1; i>=k; j++,i--)
            dividend[j] = in3[i] - '0';
        delen -=k;
        return 1;
    }
    
    /*
        减法操作函数
    
        参数:
            int* ans1 : 被减数,并且勇于带回相减的结果
            int* ans2 : 减数
            int len1 : 被减数长度
            int len2 : 减数长度
        返回值
            -1 : 被减数小于减数
            a>=0 : 所求结果ans1的长度
    */
    int Sub(int* ans1,int* ans2,int len1,int len2)
    {
        int i,j;
    
        //以下判断ans1是否大于ans2,不大于则返回-1
        if( len1<len2 )
            return -1;
        else if(len1 == len2)
        {
            for( i=len1-1; i>=0; i-- )
            {
                if(ans1[i]<ans2[i])
                    return -1;
                else if(ans1[i]>ans2[i])
                    break;
            }
        }
    
        for( i=0; i<len1; i++)
        {//要求调用本函数时确保党i>=len2时,ans2[i] = 0
            ans1[i] -= ans2[i];
            if( ans1[i]< 0)//借位
            {
                ans1[i] += 10;
                ans1[i+1]--;
            }
        }
        for( i=len1-1; i>=0 && ans1[i]==0; i--);//去掉前导0
    
        return i+1;
    
    }
    
    void Output(int* ans,int len)
    {
        int i;
        for(i=len-1; i>=0; i--)
            printf("%d",ans[i]);
        printf("
    ");
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    
        int divisor[LEN],dividend[LEN],result[LEN],len1,len2,len3;
        char operate;
    
        while(1)
        {
            memset(divisor,0,sizeof(divisor));
            memset(dividend,0,sizeof(dividend));
            if( Input(divisor,dividend,operate,len1,len2) == 0 )break;
    
            //如果被除数小于除数可以直接得到结果
            if( len1 < len2)
            {
                if( operate == '/' )
                    printf("0
    ");
                else if(operate == '%')
                    Output(divisor,len1);
                continue;
            }
    
            int Times = len1 - len2;//被除数比除数多几位
            int i;
            if(Times>0)//减数低位补0
            {
                for( i=0; i<len2; i++)
                    dividend[i+Times] = dividend[i];
                for(i=0; i<Times; i++)
                    dividend[i] = 0;
                len2 = len1;
            }
    
            int temp;
            memset(result,0,sizeof(result));
            for(i=0; i<=Times; i++)
            {
                //一直减到不够减为止
                //先减去若干个 dividend×(10 的 Times 次方),
                //不够减了,再减去若干个 dividend×(10 的 Times-1 次方),......
                while((temp = Sub(divisor,dividend+i,len1,len2-i))>=0)
                {
                    len1 = temp;
                    result[Times-i]++;
                }
            }
    
            if(operate == '/')
            {
                for(i=LEN-1; i>=0 && result[i]==0; i--);
                Output(result,i+1);
            }
            else if(operate == '%')
            {
                for(i=LEN-1; i>=0 && divisor[i]==0; i--);
                Output(divisor,i+1);
            }
        }
    
        return 0;
    }
  • 相关阅读:
    POJ 3923 Ugly Windows(——考察思维缜密性的模拟题)
    POJ 3829 Seat taking up is tough(——只是题目很长的模拟)
    练习json读取中文
    关于调接口和腾讯云cos方面。
    JavaScript如何处理解析JSON数据详解
    js获取url参数值的两种方式
    修改Host,配置域名访问
    Atom设置震撼的编辑效果
    atom总结
    用node.js可以开启静态服务 不需要借助apache 或者xampl
  • 原文地址:https://www.cnblogs.com/fanling999/p/3931645.html
Copyright © 2011-2022 走看看