大數運算(1)—— 加法篇


 前言

  最近遇到一道求階乘的題目,原以為極其簡單,但是階乘的結果超過了范圍最大的基本數據類型的范圍,於是就着手研究大數運算(large number computing),本篇先介紹大數加法。

原理

  大數運算的原理其實就是模擬人工計算(注記:再考慮是否有其他算法。注記日期:2017.3.19),人工加法計算步驟如下:

    1.將兩個操作數(operand)位數對齊。

    2.從最低位開始,計算兩個操作數每位的總和再加上進位數(第一位數為0)的結果,保留其個位數。

    3.若上述的結果大於10,進位數置為1,否則為0。(每位數相加的結果不會超過 9 + 9 = 18 , 因此進位數只有1和0)

    4.重復上述步驟2、3,直到計算完兩個操作數其中那個位數小的最高位。

  知曉了步驟,就需要考慮設計儲存操作數的數據結構。首先使用列表是毋庸置疑的,而順序結構和鏈表結構其實都行,這里為里減少代碼量和說明問題,暫且使用順序結構

  綜上,給出操作數的結構體如下:

1 typedef struct operand
2 {
3     int data[100];
4     int digit;    // 記錄該數位數
5 } OPERAND;

  注意,這里給出最大位數為100,下面給出的代碼中沒有考慮溢出問題,請讀者自行解決。

實現

  首先,思考上述步驟1的對齊問題,可以定義一個偏移量(offset)來存儲兩個操作數的位數之差,之后遍歷的時候可以給位數少的增加偏移量來達到對齊目的。可是這種做法會增加一定的代碼量,因此不妨試着把數字倒着存放,即數組第一個元素存儲操作數的最低位(注意前述做法的前提與此相反),這樣做就省去了寫對齊的代碼。

  下面給出該出加法函數,其中結果保存到第一個操作數中

 1 void plus(OPERAND* operand1,OPERAND* operand2)
 2 {
 3     int carry = 0;    // 進位標記
 4     int i = 0 , j = 0;
 5     for(;i < operand1->digit || j < operand2->digit;i++ , j++)
 6     {
 7         int sum = operand1->data[i] + operand2->data[j] 
 8                   + carry;    // 計算該位數總和加上進位數
 9         operand1->data[i] = sum % 10;    // 更新該位數的數據
10         carry = (sum >= 10)? 1 : 0 ;    // 判斷是否進位
11     }
12     operand1->digit = (operand1->digit > operand2->digit)?
13                        operand1->digit : operand2->digit ;
14                        // 更新位數
15     if(carry)    // 若最后需要進位
16     {
17         operand1->data[operand1->digit] = 1;
18         operand1->digit += 1;
19     }
20 }

  其中,sum % 10 用於求個位數,還有注意最后別忘了最高位的進位問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM