前言:
在計算機中數字表示的范圍是有限制的,比如我們熟知的 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\n +\n%s\n =\n", num1, num2); 52 // 反向輸出求和結果 53 for (i = len-1; i >= 0; i--) 54 printf("%d", sum[i]); 55 printf("\n"); 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\n -\n%s\n =\n", 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("\n"); 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\n *\n%s\n =\n", num1, num2); 62 for(i = len-1; i>=0; i--) 63 printf("%d", sum[i]); 64 printf("\n"); 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] = '\0'; // 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\n ÷\n%s\n =\n", 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("\n"); 133 134 return 0; 135 }
5、使用Java提供的類
在Java中提供了BigInteger類和BigDecimal類,分別用來處理大整數和大浮點數,我們只要調用里面提供的方法就能很方便的進行大數的四則運算,具體實現可參考:http://www.cnblogs.com/wuqianling/p/5410218.html