C语言大数运算-乘除法篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gd007he/article/details/69055031

前言:
这是第三篇博客,也是一次介绍二个计算的博客,可能难度会比前两篇博客大一点,所以建议对于初学者来说一定要看完我的前两篇博客再来看本篇博客,关于本次实验的环境,和思想在第一篇博客已经简单介绍过了,所以不再赘述,我会先介绍大数的乘法载介绍大数的除法,乘法的难点在于要使用一个嵌套循环,除法的难点在于一个字使用符串比较方法的技巧,本次还是会将算法都写成函数,然后在main()函数中调用,原因是在第四篇我们要将整个大数运算的方法做成自己的一个库文件,可以供自己或他人使用。

大数乘法:
由于乘法可以互换所以对于输入的数字没有限制条件,计算方法还是模仿手工算法,由被乘数的低位开始和乘数的每一位相乘并且要将大于9的十位数向前进一位,存在3个问题需要我们解决。

问题:
1 我们要用多大的数组存储结果?
2 要使用嵌套循环吗?
3 如何在计算的过程中保证进位?

其实问题也很好解决,前两个问题都可以看出答案,最后一个问题和前两篇博客的进位问题很相似,所以简单的说明后再看注释的代码是很好懂的。
1 二个数相乘最大的位数是两个乘数的位数之和。
2 很明显由于乘法的特性使用嵌套循环很合适。
3 在大数加减中执行完毕后再对存储结果的result数组进行一次进位,但在乘法中我们需要每执行一趟就要对数组进行进位的处理。

实现:
下面是全面的含有注释的代码。

  1 //#include"big.h"
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 char * bigmul(char *m,int lena,char *f,int lenb){  //乘法运算函数。
  6    int i,j,k,lensum,tmp_result,carry,num='0';
  7    lensum=lena+lenb;                               //确定结果数组的长度。
  8    for(i=0;i<lena;i++){                            //将ASCII码转为对应的数字存储。
  9       m[i]=m[i]-num;
 10    }
 11    for(i=0;i<lenb;i++){
 12       f[i]=f[i]-num;
 13    }
 14    char *result,final[BUFSIZ];
 15    result=(char*)calloc(lensum,1);
 16    for(i=0;i<lenb;i++){                      //为被乘数作一趟乘法。
 17       for(j=0;j<lena;j++){
 18          tmp_result=f[lenb-i-1]*m[lena-j-1];
 19          result[j+i]+=tmp_result;
 20       }
 21       for(k=0;k<=j+i-1;k++){                 //每作一趟乘法整理一次结果数组。
 22          if(result[k]>9){
 23             carry=result[k]/10;
 24             result[k]=result[k]%10;
 25             result[k+1] += carry;
 26          }
 27       }
 28    }
 29    j=0;
 30    if(result[lensum-1]!=0){                  //去除前导零将结果整理到final数组中。
 31       final[j]=result[lensum-1]+num;
 32       j++;
 33    }
 34    for(i=lensum-2;i>=0;i--){
 35       final[j++]=result[i]+num;
 36    }
 37    result=final;                             //将指针指向final数组并返回该指针。      
 38    return result;
 39 }
 40 int main(){                                                 //利用main测试方法,用puts打印结果。               
 41    int lena,lenb;
 42    char *result,sa[BUFSIZ],sb[BUFSIZ];
 43    scanf("%s",sa);
 44    scanf("%s",sb);
 45    lena=strlen(sa);
 46    lenb=strlen(sb);
 47    result=bigmul(sa,lena,sb,lenb);
 48    puts(result);
 49 
 50 }

下面是大数除法。

前言:
大数除法的难点在于思考算法,可以用连续的减法来实现,举个简单了例子:32/2可以用32连续减去2每减一次i加一,当差小于被减数时停止。i即为商,由于我们前面实现了大数减法所以用该方法可以实现,但是有一个问题就是如果用一亿除以一那么就需要执行一亿次,况且我们做的是大数数算,输入100位以上的数也都是有可能的,那么计算的时间就是几天,几年,几万年都有可能。所以只有模仿手工的方法,从高位开始计算。32/2从高位先用3-2只能减1次,将余数保留变成12/2,可以减6次,从而得到结果16极大的降低了循环减的次数。

大数除法:
有很多问题大多都是的我们前面遇到的问题,例如结果数组的位数,对数组的整理进位问题,嵌套循环和乘法相同按趟执行,既然是相似的问题我就不再说了。

注意:
除法对数据有限制不能分母为零,分母为零没有意义,不能用小数除以大数,因为小数除以大数本质还是大数除以小数结果加个分之一就可以了。
返回的结果是保存商的数组的指针,不包含余数。

实现:
下面是完整的含有注释的代码,如果想判断输入,或输入余数,可自行修改代码。

  1 //#include"big.h"
  2 #include<stdio.h>
  3 #include<string.h>
  4 char diva[BUFSIZ],divb[BUFSIZ];
  5 char result_tmp[BUFSIZ];
  6 char final[BUFSIZ];
  7 char * bigdiv(char *diva,int lena,char *divb,int lenb){        //大数除法函数。
  8     int i,j,k;
  9     char  * result;
 10 /*          if((lena<lenb||lena==lenb)&&strcmp(diva,divb)<0){  //去除了以小除大的判断
 11                 printf("0 余数=");//求余数  
 12                 for(i=0; i<lena; i++)  
 13                     printf("%d",diva[i]-'0');  
 14                 printf("\n");  
 15                 return result;
 16             }  
 17 */
 18             k=0;
 19             while(1){                        //死循环只有当lena和lenb相等时跳出循环,因为会不断的在divb数组前加0所以该数组的长度,
 20                                              //会不断的变化当两者相等时说明已经无法在作减法。
 21                 result_tmp[k]=0;
 22                 while(strcmp(diva,divb)>=0){ //用字符串比较的方法是一个亮点,很巧妙。因为strcmp()比较的方式是从前到后依次比较 
 23                  int i=0,j;                  //如果相等则向后移动一位一旦发现不等则立即返回忽略后面的所有数据。
 24                      while(1){
 25                         if(diva[i]=='0') i++;//去除diva高位前面的0
 26                         else{
 27                            j=i;              //去除divb高位填充的0
 28                          break;  
 29                         }  
 30                }
 31     
 32                for(; i<lenb; i++)            //作减法
 33                diva[i]=diva[i]-divb[i]+'0';
 34                for(i=lenb-1; i>j; i--)       //每作一组减法就整理数组,这种整理数组在前几篇中都有使用。
 35                if(diva[i]<'0'){              //不过在这里不是整理结果数组而是diva数组,结果保存在a数组中不用整理
 36                   diva[i]+=10;;
 37                   diva[i-1]--;
 38                }
 39                     result_tmp[k]++;
 40 
 41                }
 42                   k++;
 43                   if(lena==lenb)   break;
 44                   for(i=lenb-1; i>=0; i--)   //将divb中的元素先后移位,同时扩大divb长度并且在divb前端补一位0。
 45                     divb[i+1]=divb[i];
 46                   divb[0]='0';               //由于数组后移所以divb[0]每次移动后都为空,所以每次用0补齐。
 47                   lenb++;
 48                   divb[lenb]='\0';           //在结尾加上字符串的结束标记。
 49             }
 50             i=0;j=0;
 51             while(result_tmp[i]==0) i++;
 52             for(; i<k; i++){
 53                 final[j++]=result_tmp[i]+'0';
 54 
 55             }
 56             result=final;
 57 /* 
 58             printf(" 余数=");  
 59             j=0;//求余数  
 60             while(diva[j]=='0')  j++;  
 61             if(j==lena)  
 62             {  
 63                 printf("0\n");  
 64                 continue;  
 65             }  
 66             for(; j<n; j++)  
 67                 printf("%d",diva[j]-'0');  
 68             printf("\n");  
 69       */
 70 
 71             return result;
 72 
 73 }
 74 int main(){                                                 //利用main测试方法,用puts打印结果。               
 75    int lena,lenb;
 76    char *result,sa[BUFSIZ],sb[BUFSIZ];
 77    scanf("%s",sa);
 78    scanf("%s",sb);
 79    lena=strlen(sa);
 80    lenb=strlen(sb);
 81    result=bigdiv(sa,lena,sb,lenb);
 82    puts(result);
 83 
 84 }

所有的运算到此完结最后一篇做一个大数运算库,有时间我会把库放到github如果以后有时间我会做些优化并且加入对浮点的支持。

阅读更多
换一批

没有更多推荐了,返回首页