大数问题(高精度运算)

一、基本概念

  在某些情况下,我们必须处理相当大的一个整数,运用类型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("\n");
}

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(‘\n‘);
}

/*
    加法操作函数

    参数:
        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:\n",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\n");
        return;
    }
    for(; i>=0; i--)
        printf("%d",ans[i]);
    printf("\n");
}

/*
    乘法操作函数

    参数:
        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("\n");
}

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\n");
            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;
}
时间: 2024-12-16 15:15:56

大数问题(高精度运算)的相关文章

hdu4927 Series 1(组合+公式 Java大数高精度运算)

题目链接: Series 1 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 423    Accepted Submission(s): 146 Problem Description Let A be an integral series {A1, A2, . . . , An}. The zero-order series o

大数高精度运算(模板)

前言:高精度运算.是指參与运算的数(加数.减数,因子--)范围大大超出了标准数据类型(整型,实型)能表示的范围的运算. 模板:包含大数加减乘除.大数与int数的乘法,模板能够不断扩充. 代码: /* 所有亲測可用,可是不能用于负数的运算,仅仅能对正数进行大数运算 */ const int ten[4]= {1,10,100,1000}; const int maxl = 300; struct BigNumber { int d[maxl]; char s[maxl]; BigNumber(co

[算法]大数问题(高精度运算)

[大数相加] [代码一] /********************************* * 日期:2015-01-28 * 作者:SJF0115 * 题目: 大数加法(高精度加法) * 博客: **********************************/ #include <iostream> using namespace std; string AddString(string num1,string num2){ int len1 = num1.length(); in

[入门]高精度运算

(本人知识 while(1)cout<<"非常"; 有限,如果你看到我有任何错漏或者不足,真的真的真的恳请大家指出,蟹蟹,我希望大家可以一起进步~) int类型的变量只能存放-2^31~2^31-1范围的数据 long long类型的变量只能存放-2^63~2^63-1范围的数据 对于大数阶乘这种肯定是存不下,因此我们需要用数组存放数据: 下面是一个高精度运算的例子: 题目描述 用高精度计算出S=1!+2!+3!+…+n! (n≤50)S=1!+2!+3!+…+n!(n≤5

高精度运算

在开发中高精度运算使用并非非常频繁,float和double的精度实际上能够满足绝大多数需要,且高精度运算效率比常规的运算要慢一点,但使用高精度运算往往是为了使用其中便捷的API.官方提供的高精度运算的类主要两个 BigInteger:当数值范围超过long时使用 BigDecimal:几乎涵盖BigInteger功能,还可以保留小数点任意位数,理论上精度无穷大,以下主要说明此类 注:两者API很相似 加减乘除 BigDecimal aaa = new BigDecimal(20); BigDe

高精度运算1

1.高精度运算_加法 AYYZOJ p1443 COGS p37 1 type 2 arr=array[1..200]of integer; 3 var 4 a,b:arr;i,la,lb:integer; n:string; 5 procedure add(a,b:arr;la,lb:integer); 6 var i,x,lc:integer; c:arr; 7 begin 8 i:=1; x:=0; 9 while (i<=la) or(i<=lb) do 10 begin x:=a[i

高精度运算专题3-乘法运算(The multiplication operation)

这个专题呢,我就来讲讲高精度的乘法,下面是三个计算乘法的函数,第一个函数是char类型的,要对字符串进行数字转换,而第二个是两个int类型的数组,不用转换成数字,第三个则更为优化,用a数组-b数组放回数组a里面 函数1思路:要先把char类型的转换成int类型的数,直接每个数-‘0’就可以实现把char类型的转换成int类型的了. ①记录数组a.数组b的长度,放到第一位 ②每个位相乘,用一个数来记录进位(初值为0),每个位相乘,加上进位,存入c数组的相对应的位置,每次进位要重新赋值 ③最后记得要

字符串大数加减运算问题

这里自己利用STL模板中的容器和链表实现字符串大数加减运算. 1 #include<iostream> 2 #include<vector> 3 #include<list> 4 using namespace std; 5 6 list<char> Input_Number(list<char> ilist)//输入链表字符串,以‘#’作为结束符 7 { 8 cout<<"please Input string ,end

整数高精度运算——加法

高精度运算是信息学的一种重要算法.这种算法使用多个存储单位进行计算,因此它的计算范围超过一般使用一个存储单位的算法.也是一些信息学竞赛的常考题目. 高精度运算主要有以下几个步骤: 1.读取字符串,转换成数字倒序存储到整数数组中: 2.运算,注意进位和借位: 3.倒序输出整数数组,加法注意最高位进位,减法注意高位中的无用的0不要输出: 高精度加法代码: #include<stdio.h>#include<string.h>char s[1000];       //数组比较大时,应作

算法学习笔记(三)问题的转化与高精度运算

问题:设购票点没有任何的零钱,票价50美元,现有m人手持50美元,n人手持100美元,求这样m+n个人构成的队伍有多少种排队方法可以使得整个售票过程不中断. 分析:对于这个问题,经过简单的模拟可以发现,每个手持100的前面必须有一个手持50的,同样如果有k个手持100的连续出现,则前面至少连续k次50. 这样一来,可以设手持50元的为+1,手持100元的为-1,设ai为为第i个人所对应的值,则问题转化为数组的部分和a1+a2+...+ak≥0,其中k≤m+n,为了求这样的数列的个数,需要使用组合