1. 程式人生 > >大整數加法和乘法(C#)

大整數加法和乘法(C#)

昨天晚上做夢,夢到Java老師講演算法,對我提問,讓我給出大整數的加法和乘法思路。醒來抽空把它們做出來了。

其實這兩個都不難,關鍵在於儲存和讀取,計算的部分仿照小學生的做法就行了...

先看加法吧。leetcode上我做過連結串列模擬的大整數加法,用一個node儲存數字的一位,連結到一起表示整個整數。連結串列有兩個缺點:1,把數字串轉化成連結串列比較麻煩。2,做加法要將個位對齊,連結串列做這個也比較麻煩。leetcode中的題目,是把數字逆著儲存的,個位放在首部,最高位放在末端,這樣就不需要手動對齊了。

我今天是用字串來儲存大整數的。先把要相加的兩個數對齊,從最低位開始,兩個數各取一位相加,再加上上一位加法的進位,記錄本位和進位,本位儲存到結果,進位留著給下一位加法用。如果兩個數位數(長度)不一致,在短的那個數前面補零。一直加到最高位,判斷最高位有沒有向前再進位。有的話,還要記錄這個進位。

加法的C#實現:

 static string Add(string str1, string str2)
        {
            //記錄最終結果
            string rst = "";
            //i和j用於位數對齊
            int i = str1.Length-1, j = str2.Length-1, carry = 0;
            int tempI = 0, tempJ = 0, tempR = 0;
            while (i >= 0 || j >= 0)
            {
                //採集數字。長度不夠的,高位用0補
                if (i >= 0)
                    tempI = str1[i--] - '0';
                else
                    tempI = 0;

                if (j >= 0)
                    tempJ = str2[j--] - '0';
                else
                    tempJ = 0;
                //加法
                tempR = tempI + tempJ + carry;
                carry = tempR > 9 ? 1 : 0;
                tempR %= 10;
                tempR += '0';
                //本位留存到結果
                rst = (char)tempR + rst;
            }
            //最高位的向前進位
            if (carry == 1)
                rst ='1' + rst;
            return rst;
        }

乘法要複雜一些,但是跟筆算乘法是一樣的:拿其中一個數的每一位分別去乘另一個數,然後把所有的中間結果相加,得到最終結果。需要注意的是,不同位上的數,含義是不同的:個位上的a,就是數a;千位上的數b,是b*1000。所以相乘的時候,要注意在末尾補零,而記錄補零的數量也比較麻煩。由於(b*1000)*B=b*(B*1000),我把補零的操作放到另一個運算元上。

乘法的C#實現:

       static string Mlt(string str1, string str2 )
        {
            string rst = "0";
            int i = str1.Length-1;
            //迴圈用str1的每一位去乘str2
            while(i>=0)
            {
                //temp儲存單輪的結果
                string temp = "";
                //curr本位,carry進位
                int curr = 0, carry = 0;
                int j = str2.Length - 1;
                while(j>=0)
                {
                    curr = (str1[i] - '0') * (str2[j] - '0')+carry;
                    carry = curr / 10;
                    curr = curr % 10;
                    curr = curr + '0';
                    temp = (char)curr + temp;
                    j--;
                }
                if(carry!=0)
                {
                    temp = (char)(carry + '0') + temp;
                }
                //做完一輪乘法,先把中間結果加到最終結果
                rst = Add(rst, temp);
                //開始下一輪乘法,提前補零。
                str2 = str2 + '0';
                i--;
            }
            return rst;
        }

假定做運算的兩個數長度分別是m、n,那麼加法的時間複雜度是 O(max(m,n)),空間複雜度是O(1)。乘法的時間複雜度是O(m*n),空間複雜度是O(max(m,n))。

我這裡是一邊拆數字下來,一邊計算的。我想過先把它們全部拆成單個數字再來計算,不過時間複雜度沒有優化,反而空間複雜度會增加。

還可以把大整數拆成一段一段地計算,估計會快一點,不過那要先把數字串按段拆成在計算範圍內的整數,而中間的思路跟我這個應該差不多,可以認為我的做法是“一段恰好只有1位”這樣的特殊情況。

另外我沒有考慮負數和小數的情況,下次再做吧...