c语言 大数加法、阶乘和乘法

一.大数加法

定义两个足够大的数字,其数值远超过long的取值范围,设该大数的位数有两百位,求其相加所得

大数加法的核心思想详见此链接,内有详细的动画演示,这里不再赘述

大数加法、大数阶乘

直接上代码:

#include<string.h>
#include<stdio.h>
#define N 10//定义当前一个足够大的数字为10位,可任意更改
void print_num(int a[],int n)
{
    int i=n-1;//从逆序数组的最后一项开始查找,进行反逆序
    while(a[i]==0)//由于规定的数组比真实计算的数字大,所以数组最后几位必定存在0的情况
        --i;//这种情况下一定要将0舍去,否则会抬高数组的位数
    for(;i>=0;i--)//找到非零的数组,进行反逆序输出
        printf("%d",a[i]);
}
void plus(int num1[],int num2[],int n)
{//尤其注意!由于数组是逆序的,所以num[0]是个位,num[1]是十位,num[2]是百位
    for(int i=0,up=0;i<n;i++)//算法参考小学加法,这里定义一个up进位标记
    {
        int temp=num1[i]+num2[i]+up;//up最开始设为0,因为在个位无法获取进位
        num1[i]=temp%10;//若产生进位行为,则选取个位部分赋给num1
        up=temp/10;//在个位上,若个位相加产生进位,则用temp/10取整加到下一次的十位上
    }
    print_num(num1, n);
}
int main(){
    char buffer1[]="123456";//缓冲数组,将当前数组倒序写入num1中
    char buffer2[]="78951234";//同上,写入num2中
    int num1[N]={0};//将num1,2全部置为0,用来将缓冲数组写入到num数组当中
    int num2[N]={0};
    int n=N;//定义上述两个数组的长度为10
    for(int i=0,temp=(int)strlen(buffer1)-1;temp>=0;temp--)
        num1[i++]=buffer1[temp]-'0';//用倒序的方式将缓冲数组写入num中,意味着num的第一位是个位,第二位是十位,三是百位...
    for(int i=0,temp=(int)strlen(buffer2)-1;temp>=0;temp--)
        num2[i++]=buffer2[temp]-'0';
    
    plus(num1, num2, n);//将两数字相加
    printf("\n");
}

 

二.大数阶乘

大数阶乘的中心思想参考上述视频和一篇博客,博客详情:大数阶乘运算

但是,这里需要说明一个点:

1*2=2,将2存到a[0]中,

接下来是用a[0]*3;

    2*3=6,将6储存在a[0]中,

接下来是用a[0]*4;

    6*4=24,是两位数,那么24%10==4存到a[0]中,24/10==2存到a[1]中,

接下来是用a[0]*5;a[1]*5+num(如果前一位相乘结果位数是两位数,那么num就等于十位上的那个数字;如果是一位数,num==0)

    24*5=120,是三位数,那么120%10==0存到a[0]中,120/10%10==2存到a[1]中,120/100==1存到a[2]中

由于上述博客存在某些地方没有说清楚的情况,这里解释一下

关于上述博客的Q&A:

1.这里的num指的是什么?

答:这里的num指的是前面两数值相乘后进位数位多少:例如6*4得24,这里的num值的是24/10=2,进位数为2,num=2

2.下面代码为什么要充i=2开始?

答:如果这里看懂了代码其实理解2不是很难,但是没有看懂是真的难懂:

首先明确一点:5的阶乘是1*2*3*4*5,我定义的value数组的第一位为1,而我的i是从2起的,这样以来不就直接凑出了1*2了吗?当我的i自增到3,我直接在value数组中找出1*2的值,拿他们去和3相乘,也就凑成了1*2*3了

3.如何在代码当中表现出进位的思想?

答:我们以5!为例,当计算到1*2*3*4的时候,value当中的表现形式是42000000,从左到右依次是个位,十位,百位,千位...etc

(value表示的是24这个数字)

我们在关于j的循环当中拿i=5去和value数组求乘积:

5先和位于个位的4求积得20:20%10得0,0放入个位中;20/10得2,进位为2,up=2。(此时个位为0)

j++后,5再和value中的value[1]=2做乘积并且加上up进制:5*2+2=12,12%10=2放入十位,12%10=1放入up中(此时十位为2)

j++后,5再和value中百位value[2]=0做乘积同时加上up:5*0+1=1,1%10=1放入百位,1/10=0放入up中(此时百位为1)

j++,5再和千位做运算,运算结果为0,重复上述循环一直到关于i=5的j循环结束,开始下一轮。

4.若上述举例子的不再是5而是20呢?最后几项如何处理

答:假设1*2...19为止其结果==‘56’(纯假设),

那么最后一项如何处理:56*20?

首先拿个位6和20相乘得120,120%10得0,0放到个位;120/10得12,up=12;(个位为0)

十位5和20相乘+up=112,112&10=2,2放到十位,112/10得11,up=11;(十位为2)

百位0与20相乘+up=11,11&10=1放到百位,11/10=1,up=1;(百位为1)

千位0与20相乘+up=1,1&10=1放到千位,1/10=0,up=0;(千位为1)

下面全都是0了,循环一直持续到value数组的末尾,方法同上一致。

最后value数组为0211000000

倒序之后就是1120,即56*20的结果就是1120。

 

#define n 13
int main(){
    int number=11;//定义要求阶乘的数位number
    int value[n]={1};
//将存放number的阶乘后结果的数组定义为value[]数组
//根据算法,阶乘的第一项为1,因为阶乘运算中最小的数字为1
    
    for(int up=0,i=2;i<=number;i++)//定义俩变量up和i,up负责进制,i是阶乘当中的数字,i逐渐增大
        for(int j=0;j<n;j++)//我们是拿1*2*3*...,首先拿value[0]=1和2乘
        {
            int temp=value[j]*i+up;
            value[j]=temp%10;
            up=temp/10;
        }
    
    int i=n-1;//value是一个倒序的数组,第一位是个位,所以输出是需要倒置的
    while(value[i]==0)
        --i;
    for(;i>=0;i--)
        printf("%d",value[i]);
    printf("\n");
}

三.大数乘法

两个数值远大于long long的表示范围的数字相乘,用程序表示出来

这里我模拟正常运算的对位相乘思想,但是把最后一步相加进位的步骤单独抽出来写出来,用convert_normal函数表示

具体演示过程参考该视频前几分钟的讲解:高精度乘法

在传统乘法中需要两数值对位运算,例如357*384:

当位于个位上的4与7,5,3相乘的时候,其结果必须对应个位,十位,百位

当位于百位上的8与7,5,3相乘的时候,结果必须要对应十位,百位,千位

这也是为什么我要在multiply函数中引入k这个变量,k起到对位的作用。

这个算法当中,将最后一步(即3排数字的相加进位过程抽离出来,当读写成一个convert_normal函数)

在multiply函数中,result所得的结果直接将这三排数字对位相加,结果为28,76,73,39,9

接着在normal函数中这些数字全按照上面大数加法的思想转化为正常数字

#include<string.h>
#define N 10
void transition(char buffer[],int num[])//把输入的数组从char类型转为int类型
{
    for(int i=(int)strlen(buffer)-1,j=0;i>=0;i--)
        num[j++]=buffer[i]-'0';
}
void bigdata_multiply(int num1[],int num2[],int after[])//大数乘法运算
{//函数模拟乘法的过程
    for(int i=0;i<N;i++)//num1
    {
        int k=i;
        for(int j=0;j<N;j++)//num2
            after[k++]+=num1[i]*num2[j];
    }
}
void print(int num[])
{
    int i=N-1;
    while(num[i]==0)
        --i;
    for(;i>=0;i--)
        printf("%d",num[i]);
}
void convert_normal(int number[])//转换函数,将result数组中的数值转化为正常数字
{
    for(int i=0,up=0;i<N;i++)
    {
        int temp=number[i]+up;
        number[i]=temp%10;
        up=temp/10;
    }
}
int main(){
    char buffer1[N];//定义一个过渡数组
    char buffer2[N];
    int result[N]={0};//定义计算结果的数组
    int num1[10]={0};//定义第一个数字
    int num2[10]={0};//定义第二个数字
    
    printf("num1=");
    scanf("%s",buffer1);
    printf("num2=");
    scanf("%s",buffer2);
    
    transition(buffer1, num1);//将num1,2数组逆置转换为int数组
    transition(buffer2,num2);
    
    bigdata_multiply(num1,num2,result);
    convert_normal(result);
    print(result);
    printf("\n");
}

 

posted @ 2020-08-25 17:19  雾漫大武汉  阅读(1543)  评论(0编辑  收藏  举报