大数算法-【加法,减法,乘法,除法】

1、大数加法

    两个大数我们可以用数组来保存,然后在数组中逐位进行相加,再判断该位相加后是否需要进位,为了方便计算,我们将数字的低位放在数组的前面,高位放在后面。

#include<stdio.h>
#include<string.h>
#define M 10000
int main()
{
    char s1[M],s2[M];
    int a[M],b[M],c[M];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        memset(c,0,sizeof(c));
        int i,j,k;
        int len1,len2;
        len1=strlen(s1);
        len2=strlen(s2);
        for(i=0,j=len1-1;j>=0;i++,j--)  //字符转换为数字
        {
            a[i]=s1[j]-'0';
        }
        for(i=0,j=len2-1;j>=0;i++,j--)
        {
            b[i]=s2[j]-'0';
        }
        k=len1>len2? len1:len2;
        for(i=0;i<=k;i++)  //这里循环要==k 因为最后一位的加法可能有进位
        {
            c[i]+=a[i]+b[i];
            if(c[i]>9)   //大于9则进位
            {
                c[i]=c[i]%10;
                c[i+1]++;
            }
        }

        i=k;
        while(c[i]==0) i--;  //将首位的0删掉
        if(i<0)
        {
            printf("0");
        }
        else
        {
            for(;i>=0;i--)
                printf("%d",c[i]);
        }
        printf("\n");
    }
    return 0;
}

二、大数减法

    相减算法也是从低位开始减的。先要判断被减数和减数哪一个位数长,若被减数位数长是正常的减法;若减数位数长,则用被减数减去减数,最后还要加上负号;当两数位数长度相等时,最好比较哪一个数字大,否则负号处理会很繁琐;处理每一项时要,如果前一位相减有借位,就先减去上一位的借位,无则不减,再去判断是否能够减开被减数,如果减不开,就要借位后再去减,同时置借位为1,否则置借位为0。

#include<stdio.h>
#include<string.h>
#define M 10000
int main()
{
    char s1[M],s2[M];
    int a[M],b[M],c[M];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        int i,j,k;
        int flag=0;  //判断正负数 0为正数 1为负数
        int len1,len2;
        len1=strlen(s1);
        len2=strlen(s2);
        if(len1<len2)   //长度大的内容放入a数组中  并立一个flag
        {
            flag=1;
            for(i=0,j=len2-1; j>=0; i++,j--) //字符转换为数字
            {
                a[i]=s2[j]-'0';
            }
            for(i=0,j=len1-1; j>=0; i++,j--)
            {
                b[i]=s1[j]-'0';
            }

        }
        else if(len1==len2)
        {
            for(i=0; i<len1; i++)
            {
                if(s1[i]==s2[i])
                    continue;
                if(s1[i]>s2[i])
                {
                    flag=0;
                    for(i=0,j=len1-1; j>=0; i++,j--) //字符转换为数字
                    {
                        a[i]=s1[j]-'0';
                    }
                    for(i=0,j=len2-1; j>=0; i++,j--)
                    {
                        b[i]=s2[j]-'0';
                    }
                    break;
                }
                else
                {
                    flag=1;
                    for(i=0,j=len2-1; j>=0; i++,j--) //字符转换为数字
                    {
                        a[i]=s2[j]-'0';
                    }
                    for(i=0,j=len1-1; j>=0; i++,j--)
                    {
                        b[i]=s1[j]-'0';
                    }
                    break;
                }
            }
        }
        else if(len1>len2)
        {
            for(i=0,j=len1-1; j>=0; i++,j--) //字符转换为数字
            {
                a[i]=s1[j]-'0';
            }
            for(i=0,j=len2-1; j>=0; i++,j--)
            {
                b[i]=s2[j]-'0';
            }
        }

        k=len1>len2? len1:len2;
        for(i=0; i<=k; i++)
        {
            c[i]=c[i]+a[i]-b[i];
            if(c[i]<0)   //小于0则借位
            {
                c[i]=c[i]+10;
                c[i+1]--;
            }
        }

        i=k;
        while(c[i]==0) i--;//将首位的0删掉
        if(i<0)
        {
            printf("0");
        }
        else
        {
            if(flag==1)
            {
                printf("-");
                for(; i>=0; i--)
                {
                    printf("%d",c[i]);
                }
            }
            else
            {
                for(; i>=0; i--)
                {
                    printf("%d",c[i]);
                }
            }

        }
        printf("\n");
        memset(c,0,sizeof(c));  //清除上一步的内容
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
    }

    return 0;
}

三、大数乘法

首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。

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

另外注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零。

#include<stdio.h>
#include<string.h>

#define MAX 1000    // 大数的最大位数 

 
/*
  大数乘法 
  参数: 
  num1为第一个因数,用字符数组保存
  num2为第二个因数
  sum数组保存相乘的结果  即:num1*num2=sum
  返回值:返回数组sum的有效长度,即计算结果的位数 
 */
int Multiplication(char num1[],char num2[], int sum[])
{
    int i, j, len, len1, len2;
    int a[MAX+10] = {0};
    int b[MAX+10] = {0};
    int c[MAX*2+10] = {0};
    
    len1 = strlen(num1);
    for(j = 0, i = len1-1; i >= 0; i--) //把数字字符转换为整型数 
        a[j++] = num1[i]-'0';
    len2 = strlen(num2);
    for(j = 0, i = len2-1; i >= 0; i--)
        b[j++] = num2[i]-'0';
    
    for(i = 0; i < len2; i++)//用第二个数乘以第一个数,每次一位 
    {
        for(j = 0; j < len1; j++)
        {
            c[i+j] += b[i] * a[j]; //先乘起来,后面统一进位
        }
    }
    
    for(i=0; i<MAX*2; i++) //循环统一处理进位问题
    {
        if(c[i]>=10)
        {
            c[i+1]+=c[i]/10;
            c[i]%=10;
        }
    }

    for(i = MAX*2; c[i]==0 && i>=0; i--); //跳过高位的0
    len = i+1; // 记录结果的长度 
    for(; i>=0; i--)
        sum[i]=c[i];
    return len; 
}

int main()
{
    int i, len;
    int sum[MAX*2+10] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位 
    char num1[] = "123456789123456789"; // 第一个大数 
    char num2[] = "123456789123456789"; // 第二个大数 
    len = Multiplication(num1, num2, sum);
    // 输出结果
    printf("%s\n  *\n%s\n  =\n", num1, num2);
    for(i = len-1; i>=0; i--)
        printf("%d", sum[i]); 
    printf("\n"); 
    return 0;
}

大数除法 (还未分析!!!)

大数除法是四则运算里面最难的一种。不同于一般的模拟,除法操作不是模仿手工除法,而是利用减法操作来实现的。其基本思想是反复做除法,看从被除数里面最多能减去多少个除数,商就是多少。逐个减显然太慢,要判断一次最多能减少多少个整数(除数)的10的n次方。
以7546除以23为例:
先用7546减去23的100倍,即减去2300,可以减3次,余下646,此时商就是300 (300=100*3);
然后646减去23的10倍,即减去230,可以减2次,余下186,此时商就是320 (320=300+10*2);
然后186减去23,可以减8次,余下2,此时商就是328 (328=320+1*8);

因为2除以23的结果小于1,而我们又不用计算小数点位,所以不必再继续算下去了。

下面是C语言的两个正大数相除的参考代码,计算结果中没有小数:

#include<stdio.h>
#include<string.h> 
#define MAX 1000    // 大数的最大位数 

// 注: 
// 本代码在以下博客代码中进行修改: 
// http://www.cnblogs.com/javawebsoa/archive/2013/08/01/3231078.html
// 


/*
  函数SubStract功能:
  用长度为len1的大整数p1减去长度为len2的大整数p2
  结果存在p1中,返回值代表结果的长度
  不够减:返回-1 , 正好够:返回0
*/ 
int SubStract(int *p1, int len1, int *p2, int len2)
{
    int i;
    if(len1 < len2)
        return -1;
    if(len1 == len2 )
    {                        // 判断p1 > p2
        for(i = len1-1; i >= 0; i--)
        {
            if(p1[i] > p2[i])   // 若大,则满足条件,可做减法
                break;
            else if(p1[i] < p2[i]) // 否则返回-1
                return -1;
        }
    }
    for(i = 0; i <= len1-1; i++)  // 从低位开始做减法
    {
        p1[i] -= p2[i];         // 相减 
        if(p1[i] < 0)           // 若是否需要借位
        {   // 借位 
            p1[i] += 10;
            p1[i+1]--;
        }
    }
    for(i = len1-1; i >= 0; i--)  // 查找结果的最高位
    {
        if( p1[i] )             //最高位第一个不为0
            return (i+1);       //得到位数并返回
    } 
    return 0;                   //两数相等的时候返回0
}


/*
  大数除法---结果不包括小数点 
  num1 被除数
  num2 除数 
  sum  商,存放计算的结果,即:num1/num2=sum
  返回数组sum的有效长度,即商的位数 
*/ 
int Division(char num1[], char num2[], char sum[])
{
    int k, i, j;
    int len1, len2, len=0;     //大数位数
    int dValue;                //两大数相差位数
    int nTemp;                 //Subtract函数返回值
    int num_a[MAX] = {0};      //被除数
    int num_b[MAX] = {0};      //除数
    int num_c[MAX] = {0};      //商 

    len1 = strlen(num1);       //获得大数的位数
    len2 = strlen(num2);
    
    //将数字字符转换成整型数,且翻转保存在整型数组中 
    for( j = 0, i = len1-1; i >= 0; j++, i-- )
        num_a[j] = num1[i] - '0';
    for( j = 0, i = len2-1; i >= 0; j++, i-- )
        num_b[j] = num2[i] - '0';

    if( len1 < len2 )          //如果被除数小于除数,直接返回-1,表示结果为0
    {
        return -1;
    }
    dValue = len1 - len2;      //相差位数
    for (i = len1-1; i >= 0; i--)    //将除数扩大,使得除数和被除数位数相等
    {
        if (i >= dValue)
            num_b[i] = num_b[i-dValue];
        else                         //低位置0
            num_b[i] = 0;
    }
    len2 = len1;
    for(j = 0; j <= dValue; j++ )    //重复调用,同时记录减成功的次数,即为商
    {
        while((nTemp = SubStract(num_a, len1, num_b+j, len2-j)) >= 0)
        {
            len1 = nTemp;            //结果长度
            num_c[dValue-j]++;       //每成功减一次,将商的相应位加1
        }
    }
    // 计算商的位数,并将商放在sum字符数组中 
    for(i = MAX-1; num_c[i] == 0 && i >= 0; i-- );  //跳过高位0,获取商的位数 
    if(i >= 0)
        len = i + 1; // 保存位数 
    for(j = 0; i >= 0; i--, j++)     // 将结果复制到sum数组中 
        sum[j] = num_c[i] + '0';
    sum[j] = '\0';   // sum字符数组结尾置0 
    return len;      // 返回商的位数 
} 


int main()
{
    int i;
    int len;                // 商的位数
    char num1[MAX] = "1234567899876543210";   // 第一个大数
    char num2[MAX] = "20160415123025";              // 第二个大数
    char sum[MAX] = {0};    // 计算结果 

    //scanf("%s", num1);      //以字符串形式读入大数
    //scanf("%s", num2);
    
    len = Division(num1, num2, sum); 
    
    //输出结果
    printf("%s\n  ÷\n%s\n  =\n", num1, num2);
    if( len>=0 )
    {
        for(i = 0; i < len; i++ )
            printf("%c", sum[i]);
    }
    else
    {
        printf("0");
    }
    printf("\n");
    
    return 0;
}

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值