• 数学问题——大整数运算


      大整数又称为高精度整数,其含义就是用基本数据类型无法储存其精度的整数。

    一、大整数的存储

      很简单,使用数组即可。例如定义 int 型数组 d[1000],那么这个数组的每一位代表了存放整数的每一位。整数的高位储存在数组的高位,整数的低位储存在数组的低位

      而为了方便随时获取大整数的长度,一般都会定义一个 int 型变量 len 来记录其长度,结构体如下:

    1 // 大整数 
    2 struct bign {
    3     int d[1000];
    4     int len;
    5 }; 

      在定义结构体变量后,需要马上初始化结构体,加上“构造函数”,代码如下:

    // 大整数 
    struct bign {
        int d[1000];
        int len;
        bign() {    // 构造函数 
            memset(d, 0, sizeof(d));
            len = 0;
        } 
    }; 

      而输入大整数时,一般都是先用字符串读入,然后再把字符串另存为至 bign 结构体。由于使用 char 数组进行读入时,整数的高位会变成数组的低位,因此需要让字符串倒着赋给 d[] 数组:

     1 // 将字符串另存至 bign 结构体
     2 bign change(char str[]) {
     3     bign a;
     4     a.len = strlen(str);        // bign 的长度就是数组的长度 
     5     int i;
     6     for(i=0; i<a.len; ++i) {    // 倒着赋值 
     7         a.d[i] = str[a.len-1-i] - '0';
     8     }
     9     return a;
    10 }

      如果要比较两个 bign 变量的大小,规则也很简单:先判断两者的 len 的大小,如果不相等,则以长的为大;如果相等,则从高位到低位进行比较,直到出现某一位不等,就可以判断两个数的大小。代码如下:

     1 // 比较两个大整数变量的大小
     2 // a 大、相等、小分别返回 1、0、-1 
     3 int compare(bign a, bign b) {
     4     if(a.len ? b.len)    return 1;        // a>b
     5     else if(a.len < b.len)    return -1;    // a<b
     6     else {
     7         int i;
     8         for(i=0; i<a.len; ++i) {                // 从高位到低位比较 
     9             if(a.d[i] > b.d[i])    return 1;        // a>b
    10             else if(a.d[i] < b.d[i]) return -1;    // a<b
    11         }
    12         return 0;        // 两者相等 
    13     }
    14 } 

    二、大整数的四则运算

      1. 高精度加法

      容易得到对其中一位进行加法的步骤:将该位上的两个数字与进位相加,得到的结果取个位数作为该位结果,取十位数作为新的进位。代码如下:

     1 // 高精度加法
     2 bign add(bign a, bign b) {
     3     bign c;
     4     int carry = 0;    // 进位
     5     int i;
     6     for(i=0; i<a.len || i<b.len; ++i) {            // 以较长的为界限 
     7         int temp = a.d[i] + b.d[i] + carry;        // 其中一位相加
     8         c.d[c.len++] = temp%10;                    // 取个位
     9         carry = temp/10;                        // 新的进位 
    10     } 
    11     if(carry != 0) {                // 有进位 
    12         c.d[c.len++] = carry;
    13     }
    14     return c; 
    15 } 

      测试一下加法代码,以下为 C 代码:

     1 /*
     2     大整数运算 
     3 */
     4 
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <math.h>
     8 #include <stdlib.h>
     9 #include <time.h>
    10 #include <stdbool.h>
    11 
    12 // 大整数 
    13 typedef struct{
    14     int d[1000];
    15     int len;
    16     // C 中 struct 不能有函数 
    17     /*bign() {    // 构造函数 
    18         memset(d, 0, sizeof(d));
    19         len = 0;
    20     }*/ 
    21 } bign; 
    22 
    23 // 大整数初始化 
    24 bign init() {
    25     bign temp = { {0},0 };
    26     return temp;
    27 }
    28 
    29 // 将字符串另存至 bign 结构体
    30 bign change(char str[]) {
    31     bign a = init();
    32     a.len = strlen(str);        // bign 的长度就是数组的长度 
    33     int i;
    34     for(i=0; i<a.len; ++i) {    // 倒着赋值 
    35         a.d[i] = str[a.len-1-i] - '0';
    36     }
    37     return a;
    38 } 
    39 
    40 // 比较两个大整数变量的大小
    41 // a 大、相等、小分别返回 1、0、-1 
    42 int compare(bign a, bign b) {
    43     if(a.len > b.len)    return 1;        // a>b
    44     else if(a.len < b.len)    return -1;    // a<b
    45     else {
    46         int i;
    47         for(i=0; i<a.len; ++i) {                // 从高位到低位比较 
    48             if(a.d[i] > b.d[i])    return 1;        // a>b
    49             else if(a.d[i] < b.d[i]) return -1;    // a<b
    50         }
    51         return 0;        // 两者相等 
    52     }
    53 } 
    54 
    55 // 高精度加法
    56 bign add(bign a, bign b) {
    57     bign c = init();
    58     int carry = 0;    // 进位
    59     int i;
    60     for(i=0; i<a.len || i<b.len; ++i) {            // 以较长的为界限 
    61         int temp = a.d[i] + b.d[i] + carry;        // 其中一位相加
    62         c.d[c.len++] = temp%10;                    // 取个位
    63         carry = temp/10;                        // 新的进位 
    64     } 
    65     if(carry != 0) {                // 有进位 
    66         c.d[c.len++] = carry;
    67     }
    68     return c; 
    69 } 
    70 
    71 // 输出大整数 
    72 void print(bign a) {
    73     int i;
    74     for(i=a.len-1; i>=0; --i) {
    75         printf("%d", a.d[i]);
    76     }
    77     printf("
    ");
    78 }
    79 
    80 int main() {
    81     char str1[1000], str2[1000];
    82     scanf("%s%s", str1, str2);
    83     bign a = change(str1);
    84     bign b = change(str2);
    85     print(add(a, b));
    86 
    87     return 0;
    88 }
    测试加法

      2. 高精度减法

      同样,对其中一位进行减法的步骤:比较被减位和减位,如果不够减,则令被减位的高位减 1、被减位加 10 再进行减法;如果够减,则直接减。最后一步要注意减法后高位可能有多余的 0,要去除它们,但也要保证结果至少有一位数。代码如下:

     1 // 高精度减法 
     2 bign sub(bign a, bign b) { 
     3     bign c = init();
     4     int i;
     5     for(i=0; i<a.len || i<b.len; ++i) {    // 以较长的为界限 
     6         if(a.d[i] < b.d[i]) {            // 不够减 
     7             a.d[i+1]--;
     8             a.d[i] += 10;
     9         }
    10         c.d[c.len++] = a.d[i] - b.d[i];
    11     }
    12     while(c.len-1 >= 1 && c.d[c.len-1] == 0) {
    13         c.len--;    // 去除高位的0,同时至少保留一位最低位 
    14     }
    15     return c;
    16 }

      最后需要指出,使用 sub 函数前要比较两个数的大小,如果被减数小于减数,需要交换两个变量,然后输出负号,再使用 sub 函数。

      3. 高精度与低精度的乘法

      所谓的低精度就是可以用基本数据类型存储的数据,例如 int 型。对某一步来说就是这么一个步骤:取 bign 的某位与 int 型整体相乘,再与进位相加,所得结果的个位数作为该位结果,高位部分作为新的进位。代码如下:

     1 // 高精度与低精度的乘法
     2 bign multi(bign a, int b) {
     3     bign c = init();
     4     int carry = 0;            // 进位
     5     int i;
     6     for(i=0; i<a.len; ++i) {
     7         int temp = a.d[i] * b + carry;
     8         c.d[c.len++] = temp%10;        // 取低位 
     9         carry = temp/10;            // 取高位作为新的进位 
    10     } 
    11     while(carry != 0) {        // carry 可能不止一位 
    12         c.d[c.len++] = carry%10;
    13         carry /= 10; 
    14     }
    15     return c;
    16 } 

      另外,如果 a 和 b 中存在负数,需要先记录下其负号,然后取它们的绝对值代入函数。

      4. 高精度与低精度的除法

      其中某一步的步骤:上一步的余数乘以 10 加上该步的位,得到该步的临时被除数,将其与除数相除,商为对应的商,余数作为新的余数留作下一步用。代码如下:

     1 // r 可以用来储存余数 
     2 int r = 0;
     3 // 高精度与低精度的除法
     4 bign divide(bign a, int b) {
     5     bign c = init();
     6     c.len = a.len;        // 商的每一位与被除数对应 
     7     int i;    
     8     for(i=a.len-1; i>=0; --i) {
     9         r = r*10 + a.d[i];
    10         c.d[i] = r/b;    //
    11         r %= b;            // 新的余数 
    12     } 
    13     while(c.len-1 >= 1 && c.d[c.len-1]==0) {    
    14         c.len--;    // 去除高位 0,并保证至少有一位 
    15     } 
    16     return c; 
    17 } 

      完整的代码如下:

      1 /*
      2     大整数运算 
      3 */
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <math.h>
      8 #include <stdlib.h>
      9 #include <time.h>
     10 #include <stdbool.h>
     11 
     12 // 大整数 
     13 typedef struct{
     14     int d[1000];
     15     int len;
     16     // C 中 struct 不能有函数 
     17     /*bign() {    // 构造函数 
     18         memset(d, 0, sizeof(d));
     19         len = 0;
     20     }*/ 
     21 } bign; 
     22 
     23 // 大整数初始化 
     24 bign init() {
     25     bign temp = { .d={0},.len=0 };
     26     return temp;
     27 }
     28 
     29 // 将字符串另存至 bign 结构体
     30 bign change(char str[]) {
     31     bign a = init();
     32     a.len = strlen(str);        // bign 的长度就是数组的长度 
     33     int i;
     34     for(i=0; i<a.len; ++i) {    // 倒着赋值 
     35         a.d[i] = str[a.len-1-i] - '0';
     36     }
     37     return a;
     38 } 
     39 
     40 // 比较两个大整数变量的大小
     41 // a 大、相等、小分别返回 1、0、-1 
     42 int compare(bign a, bign b) {
     43     if(a.len > b.len)    return 1;        // a>b
     44     else if(a.len < b.len)    return -1;    // a<b
     45     else {
     46         int i;
     47         for(i=0; i<a.len; ++i) {                // 从高位到低位比较 
     48             if(a.d[i] > b.d[i])    return 1;        // a>b
     49             else if(a.d[i] < b.d[i]) return -1;    // a<b
     50         }
     51         return 0;        // 两者相等 
     52     }
     53 } 
     54 
     55 // 高精度加法
     56 bign add(bign a, bign b) {
     57     bign c = init();
     58     int carry = 0;    // 进位
     59     int i;
     60     for(i=0; i<a.len || i<b.len; ++i) {            // 以较长的为界限 
     61         int temp = a.d[i] + b.d[i] + carry;        // 其中一位相加
     62         c.d[c.len++] = temp%10;                    // 取个位
     63         carry = temp/10;                        // 新的进位 
     64     } 
     65     if(carry != 0) {                // 有进位 
     66         c.d[c.len++] = carry;
     67     }
     68     return c; 
     69 } 
     70 
     71 // 高精度减法 
     72 bign sub(bign a, bign b) { 
     73     bign c = init();
     74     int i;
     75     for(i=0; i<a.len || i<b.len; ++i) {    // 以较长的为界限 
     76         if(a.d[i] < b.d[i]) {            // 不够减 
     77             a.d[i+1]--;
     78             a.d[i] += 10;
     79         }
     80         c.d[c.len++] = a.d[i] - b.d[i];
     81     }
     82     while(c.len-1 >= 1 && c.d[c.len-1] == 0) {
     83         c.len--;    // 去除高位的0,同时至少保留一位最低位 
     84     }
     85     return c;
     86 } 
     87 
     88 // 高精度与低精度的乘法
     89 bign multi(bign a, int b) {
     90     bign c = init();
     91     int carry = 0;            // 进位
     92     int i;
     93     for(i=0; i<a.len; ++i) {
     94         int temp = a.d[i] * b + carry;
     95         c.d[c.len++] = temp%10;        // 取低位 
     96         carry = temp/10;            // 取高位作为新的进位 
     97     } 
     98     while(carry != 0) {        // carry 可能不止一位 
     99         c.d[c.len++] = carry%10;
    100         carry /= 10; 
    101     }
    102     return c;
    103 } 
    104 
    105 // r 可以用来储存余数 
    106 int r = 0;
    107 // 高精度与低精度的除法
    108 bign divide(bign a, int b) {
    109     bign c = init();
    110     c.len = a.len;        // 商的每一位与被除数对应 
    111     int i;    
    112     for(i=a.len-1; i>=0; --i) {
    113         r = r*10 + a.d[i];
    114         c.d[i] = r/b;    //
    115         r %= b;            // 新的余数 
    116     } 
    117     while(c.len-1 >= 1 && c.d[c.len-1]==0) {    
    118         c.len--;    // 去除高位 0,并保证至少有一位 
    119     } 
    120     return c; 
    121 } 
    122 
    123 // 输出大整数 
    124 void print(bign a) {
    125     int i;
    126     for(i=a.len-1; i>=0; --i) {
    127         printf("%d", a.d[i]);
    128     }
    129     printf("
    ");
    130 }
    131 
    132 int main() {
    133     char str1[1000], str2[1000];
    134     scanf("%s%s", str1, str2);
    135     bign a = change(str1);
    136     bign b = change(str2);
    137     print(add(a, b));
    138     print(sub(a, b));
    139     print(multi(a, 11));
    140     print(divide(a, 10));
    141     printf("r = %d
    ", r); 
    142 
    143     return 0;
    144 }
    大整数运算
  • 相关阅读:
    Java学习:冒泡排序和选择排序
    Java学习:多态
    Java学习:抽象类与接口
    Java学习:继承
    Java学习:标准类
    Java学习:方法简介
    传参的本质
    new 关键字做的事
    一个引用类型的对象占多大堆空间
    栈中空间大小
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes15.html
Copyright © 2020-2023  润新知