• 大数的四则运算(加法、减法、乘法、除法)


    前言:

        在计算机中数字表示的范围是有限制的,比如我们熟知的 int、float、double 等数据类型所能表示的范围都是有限的,如果我们要对位数达到几十位、几百位、上千位的大整数进行计算,这些数据类型显然不能满足我们的要求,因此我们需要通过算法来实现这些功能。

    1、大数加法

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

    下面是两个正的大整数相加算法的C语言参考代码:

     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 #define MAX 1000    // 大数的最大位数 
     5 
     6  
     7 /*
     8   大数加法 
     9   参数: 
    10   num1为第一个大数,用字符数组保存
    11   num2为第二个大数
    12   sum数组保存相加的结果  即:num1+num2=sum
    13   返回值:返回数组sum的有效长度,即计算结果的位数 
    14  */
    15 int Addition(char num1[], char num2[], int sum[])
    16 {
    17     int i, j, len;
    18     int n2[MAX] = {0};
    19     int len1 = strlen (num1); // 计算数组num1的长度,即大数的位数 
    20     int len2 = strlen (num2); // 计算数组num2的长度,即大数的位数 
    21 
    22     len = len1>len2 ? len1 : len2; // 获取较大的位数
    23     //将num1字符数组的数字字符转换为整型数字,且逆向保存在整型数组sum中,即低位在前,高位在后
    24     for (i = len1-1, j = 0; i >= 0; i--, j++) 
    25         sum[j] = num1[i] - '0';
    26     // 转换第二个数 
    27     for (i = len2-1, j = 0; i >= 0; i--, j++)
    28         n2[j] = num2[i] - '0';
    29     // 将两个大数相加 
    30     for (i = 0; i <= len; i++)
    31     {
    32         sum[i] += n2[i];  // 两个数从低位开始相加 
    33         if (sum[i] > 9)   // 判断是否有进位 
    34         {   // 进位 
    35             sum[i] -= 10;
    36             sum[i+1]++;
    37         }
    38     }
    39     if(sum[len] != 0)  // 判断最高位是否有进位 
    40         len++;
    41     return len;   // 返回和的位数 
    42 }
    43 
    44 int main()
    45 {
    46     int i, len;
    47     int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位 
    48     char num1[] = "1234567891234567891234"; // 第一个大数 
    49     char num2[] = "2345678912345678913345"; // 第二个大数 
    50     len = Addition(num1, num2, sum);    // 两数相加 
    51     printf("%s
      +
    %s
      =
    ", num1, num2);
    52     // 反向输出求和结果
    53     for (i = len-1; i >= 0; i--)
    54         printf("%d", sum[i]);
    55     printf("
    "); 
    56     return 0;
    57 }

    2、大数减法

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

    下面是C语言参考代码:

      1 #include<stdio.h>
      2 #include<string.h>
      3 
      4 #define MAX 1000    // 大数的最大位数 
      5 
      6  
      7 /*
      8   大数减法 
      9   参数: 
     10   num1为被减数,用字符数组保存
     11   num2为减数 
     12   sum数组保存相减的结果   即:num1-num2=sum
     13   返回值:返回数组sum的有效长度,即计算结果的位数 
     14  */
     15 int Subtraction(char num1[], char num2[], int sum[])
     16 {
     17     int i, j, len, blag;
     18     char *temp;
     19     int n2[MAX] = {0};
     20     int len1 = strlen(num1); // 计算数组num1的长度,即大数的位数 
     21     int len2 = strlen(num2); // 计算数组num2的长度,即大数的位数
     22     
     23     // 在进行减法之前要进行一些预处理 
     24     blag = 0; // 为0表示结果是正整数,为1表示结果是负整数 
     25     if(len1 < len2) // 如果被减数位数小于减数
     26     {
     27         blag = 1; // 标记结果为负数
     28         // 交换两个数,便于计算 
     29         temp = num1;
     30         num1 = num2;
     31         num2 = temp;
     32         len = len1;
     33         len1 = len2;
     34         len2 = len;
     35     }
     36     else if(len1 ==len2) // 如果被减数的位数等于减数的位数
     37     {  
     38         // 判断哪个数大 
     39         for(i = 0; i < len1; i++)
     40         {
     41             if(num1[i] == num2[i])
     42                 continue;
     43             if(num1[i] > num2[i])
     44             {
     45                 blag = 0; // 标记结果为正数 
     46                 break;
     47             } 
     48             else
     49             {
     50                 blag = 1; // 标记结果为负数 
     51                 // 交换两个数,便于计算 
     52                 temp = num1;
     53                 num1 = num2;
     54                 num2 = temp;
     55                 break;
     56             } 
     57         } 
     58     }
     59     len = len1>len2 ? len1 : len2; // 获取较大的位数
     60     //将num1字符数组的数字转换为整型数且逆向保存在整型数组sum中,即低位在前,高位在后
     61     for (i = len1-1, j = 0; i >= 0; i--, j++) 
     62         sum[j] = num1[i] - '0';
     63     // 转换第二个数 
     64     for (i = len2-1, j = 0; i >= 0; i--, j++)
     65         n2[j] = num2[i] - '0';
     66     // 将两个大数相减 
     67     for (i = 0; i <= len; i++)
     68     {
     69         sum[i] = sum[i] - n2[i]; // 两个数从低位开始相减 
     70         if (sum[i] < 0)   // 判断是否有借位 
     71         {    // 借位 
     72             sum[i] += 10;
     73             sum[i+1]--;
     74         }
     75     }
     76     // 计算结果长度 
     77     for (i = len1-1; i>=0 && sum[i] == 0; i--)
     78         ;
     79     len = i+1;
     80     if(blag==1)
     81     {
     82         sum[len] = -1;  // 在高位添加一个-1表示负数 
     83         len++;
     84     }
     85     return len;   // 返回结果的位数 
     86 }
     87 
     88 int main()
     89 {
     90     int i, len;
     91     int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位 
     92     char num1[] = "987654321987654321"; // 第一个大数 
     93     char num2[] = "123456789123456789"; // 第二个大数 
     94     len = Subtraction(num1, num2, sum);    // 两数相减 
     95     // 输出结果
     96     printf("%s
      -
    %s
      =
    ", num1, num2);
     97     if(sum[i=len-1] < 0) // 根据高位是否是-1判断是否是负数
     98     {
     99         printf("-"); // 输出负号
    100         i--;
    101     }
    102     for (; i >= 0; i--)
    103         printf("%d", sum[i]);
    104     printf("
    "); 
    105     return 0;
    106 } 

    3、大数乘法

        首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。

        计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。

        总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
        ans[i+j] = a[i]*b[j];

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

    下面是C语言的两个正大数相乘的参考代码:

     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 #define MAX 1000    // 大数的最大位数 
     5 
     6  
     7 /*
     8   大数乘法 
     9   参数: 
    10   num1为第一个因数,用字符数组保存
    11   num2为第二个因数
    12   sum数组保存相乘的结果  即:num1*num2=sum
    13   返回值:返回数组sum的有效长度,即计算结果的位数 
    14  */
    15 int Multiplication(char num1[],char num2[], int sum[])
    16 {
    17     int i, j, len, len1, len2;
    18     int a[MAX+10] = {0};
    19     int b[MAX+10] = {0};
    20     int c[MAX*2+10] = {0};
    21     
    22     len1 = strlen(num1);
    23     for(j = 0, i = len1-1; i >= 0; i--) //把数字字符转换为整型数 
    24         a[j++] = num1[i]-'0';
    25     len2 = strlen(num2);
    26     for(j = 0, i = len2-1; i >= 0; i--)
    27         b[j++] = num2[i]-'0';
    28     
    29     for(i = 0; i < len2; i++)//用第二个数乘以第一个数,每次一位 
    30     {
    31         for(j = 0; j < len1; j++)
    32         {
    33             c[i+j] += b[i] * a[j]; //先乘起来,后面统一进位
    34         }
    35     }
    36     
    37     for(i=0; i<MAX*2; i++) //循环统一处理进位问题
    38     {
    39         if(c[i]>=10)
    40         {
    41             c[i+1]+=c[i]/10;
    42             c[i]%=10;
    43         }
    44     }
    45 
    46     for(i = MAX*2; c[i]==0 && i>=0; i--); //跳过高位的0
    47     len = i+1; // 记录结果的长度 
    48     for(; i>=0; i--)
    49         sum[i]=c[i];
    50     return len; 
    51 }
    52 
    53 int main()
    54 {
    55     int i, len;
    56     int sum[MAX*2+10] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位 
    57     char num1[] = "123456789123456789"; // 第一个大数 
    58     char num2[] = "123456789123456789"; // 第二个大数 
    59     len = Multiplication(num1, num2, sum);
    60     // 输出结果
    61     printf("%s
      *
    %s
      =
    ", num1, num2);
    62     for(i = len-1; i>=0; i--)
    63         printf("%d", sum[i]); 
    64     printf("
    "); 
    65     return 0;
    66 } 

    4、大数除法

        大数除法是四则运算里面最难的一种。不同于一般的模拟,除法操作不是模仿手工除法,而是利用减法操作来实现的。其基本思想是反复做除法,看从被除数里面最多能减去多少个除数,商就是多少。逐个减显然太慢,要判断一次最多能减少多少个整数(除数)的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语言的两个正大数相除的参考代码,计算结果中没有小数:

      1 #include<stdio.h>
      2 #include<string.h> 
      3 #define MAX 1000    // 大数的最大位数 
      4 
      5 // 注: 
      6 // 本代码在以下博客代码中进行修改: 
      7 // http://www.cnblogs.com/javawebsoa/archive/2013/08/01/3231078.html
      8 // 
      9 
     10 
     11 /*
     12   函数SubStract功能:
     13   用长度为len1的大整数p1减去长度为len2的大整数p2
     14   结果存在p1中,返回值代表结果的长度
     15   不够减:返回-1 , 正好够:返回0
     16 */ 
     17 int SubStract(int *p1, int len1, int *p2, int len2)
     18 {
     19     int i;
     20     if(len1 < len2)
     21         return -1;
     22     if(len1 == len2 )
     23     {                        // 判断p1 > p2
     24         for(i = len1-1; i >= 0; i--)
     25         {
     26             if(p1[i] > p2[i])   // 若大,则满足条件,可做减法
     27                 break;
     28             else if(p1[i] < p2[i]) // 否则返回-1
     29                 return -1;
     30         }
     31     }
     32     for(i = 0; i <= len1-1; i++)  // 从低位开始做减法
     33     {
     34         p1[i] -= p2[i];         // 相减 
     35         if(p1[i] < 0)           // 若是否需要借位
     36         {   // 借位 
     37             p1[i] += 10;
     38             p1[i+1]--;
     39         }
     40     }
     41     for(i = len1-1; i >= 0; i--)  // 查找结果的最高位
     42     {
     43         if( p1[i] )             //最高位第一个不为0
     44             return (i+1);       //得到位数并返回
     45     } 
     46     return 0;                   //两数相等的时候返回0
     47 }
     48 
     49 
     50 /*
     51   大数除法---结果不包括小数点 
     52   num1 被除数
     53   num2 除数 
     54   sum  商,存放计算的结果,即:num1/num2=sum
     55   返回数组sum的有效长度,即商的位数 
     56 */ 
     57 int Division(char num1[], char num2[], char sum[])
     58 {
     59     int k, i, j;
     60     int len1, len2, len=0;     //大数位数
     61     int dValue;                //两大数相差位数
     62     int nTemp;                 //Subtract函数返回值
     63     int num_a[MAX] = {0};      //被除数
     64     int num_b[MAX] = {0};      //除数
     65     int num_c[MAX] = {0};      //
     66 
     67     len1 = strlen(num1);       //获得大数的位数
     68     len2 = strlen(num2);
     69     
     70     //将数字字符转换成整型数,且翻转保存在整型数组中 
     71     for( j = 0, i = len1-1; i >= 0; j++, i-- )
     72         num_a[j] = num1[i] - '0';
     73     for( j = 0, i = len2-1; i >= 0; j++, i-- )
     74         num_b[j] = num2[i] - '0';
     75 
     76     if( len1 < len2 )          //如果被除数小于除数,直接返回-1,表示结果为0
     77     {
     78         return -1;
     79     }
     80     dValue = len1 - len2;      //相差位数
     81     for (i = len1-1; i >= 0; i--)    //将除数扩大,使得除数和被除数位数相等
     82     {
     83         if (i >= dValue)
     84             num_b[i] = num_b[i-dValue];
     85         else                         //低位置0
     86             num_b[i] = 0;
     87     }
     88     len2 = len1;
     89     for(j = 0; j <= dValue; j++ )    //重复调用,同时记录减成功的次数,即为商
     90     {
     91         while((nTemp = SubStract(num_a, len1, num_b+j, len2-j)) >= 0)
     92         {
     93             len1 = nTemp;            //结果长度
     94             num_c[dValue-j]++;       //每成功减一次,将商的相应位加1
     95         }
     96     }
     97     // 计算商的位数,并将商放在sum字符数组中 
     98     for(i = MAX-1; num_c[i] == 0 && i >= 0; i-- );  //跳过高位0,获取商的位数 
     99     if(i >= 0)
    100         len = i + 1; // 保存位数 
    101     for(j = 0; i >= 0; i--, j++)     // 将结果复制到sum数组中 
    102         sum[j] = num_c[i] + '0';
    103     sum[j] = '';   // sum字符数组结尾置0 
    104     return len;      // 返回商的位数 
    105 } 
    106 
    107 
    108 int main()
    109 {
    110     int i;
    111     int len;                // 商的位数
    112     char num1[MAX] = "1234567899876543210";   // 第一个大数
    113     char num2[MAX] = "20160415123025";              // 第二个大数
    114     char sum[MAX] = {0};    // 计算结果 
    115 
    116     //scanf("%s", num1);      //以字符串形式读入大数
    117     //scanf("%s", num2);
    118     
    119     len = Division(num1, num2, sum); 
    120     
    121     //输出结果
    122     printf("%s
      ÷
    %s
      =
    ", num1, num2);
    123     if( len>=0 )
    124     {
    125         for(i = 0; i < len; i++ )
    126             printf("%c", sum[i]);
    127     }
    128     else
    129     {
    130         printf("0");
    131     }
    132     printf("
    ");
    133     
    134     return 0;
    135 }

    5、使用Java提供的类

        在Java中提供了BigInteger类和BigDecimal类,分别用来处理大整数和大浮点数,我们只要调用里面提供的方法就能很方便的进行大数的四则运算,具体实现可参考:http://www.cnblogs.com/wuqianling/p/5410218.html

  • 相关阅读:
    AcWing 838. 堆排序
    AcWing 240. 食物链
    Sublime下载地址
    【转载】Java 8 Optional的正确姿势
    【SpringBoot】通过server.servlet.context-path设置应用的上下文路径
    【java/Lamda】List.stream().collect(Collectors.toMap(Emp::getId, a->a,(k1,k2)->k2))的意义
    Prometheus修改数据保留时间
    Atitit BLE 协议栈 艾提拉总结 目录 1. ——关于BLE的一些基本概念——
    Atitit 高并发设计实践 艾提拉著 目录 1. 并发的实现俩中模式 并发角度来看 基于事件触发vs线程的 1 2. 负载均衡 1 2.1. 云服务模型paas caas faas+http
    Atitit 锁的不同层级 app锁 vm锁 os锁 硬件锁 目录 1. 在硬件层面,CPU提供了原子操作、关中断、锁内存总线的机制 1 1.1. test and set指令 1 1.2. 锁内
  • 原文地址:https://www.cnblogs.com/wuqianling/p/5387099.html
Copyright © 2020-2023  润新知