?大數,就是C/C++中利用基本類型所不能存儲的數字,少則數十位,大則幾萬位,如何存儲和計算大數就是本文的內容。
?在C和C++中,沒有存儲大數的數據結構,就算 unsigned long long也只能表示19位的數字
?如果我們用double則會出現精度不準確的問題,如果我們想精確儲存計算大數,學習本文是必要的。
?存儲大數的方法有兩種,不管是數組還是字符串,在運算時他們的原理其實是相同的。
看下邊兩道題,帶你了解大數的計算方法
字符串相加
?這道題的思路很簡單,不能將字符串轉換為整數形式,就算轉了我們也過不了這道題。因為數據的范圍很大。
?長度為一萬的數字字符串,這就是一個很大的數了。
?解決思路:還記得我們小學時是如何進行加法運算的嗎?我們從地位算起,如果兩個加數和大于10,我們就會進一,按照這種思路依次進行計算,知道求出結果為止。
通過這種思路我們可以寫出這樣的代碼
class Solution {
public:string addStrings(string num1, string num2) {int i = num1.length() - 1, j = num2.length() - 1, add = 0;string ans = "";while (i >= 0 || j >= 0) {int x = i >= 0 ? num1[i] - '0' : 0;int y = j >= 0 ? num2[j] - '0' : 0;int result = x + y + add;ans.insert(ans.begin(),'0' + result % 10);add = result / 10;i -= 1;j -= 1;}return ans;}
};
?可以看出,計算時是從字符串末尾一次項前進行求和計算。如果該數大于10,就會將進位更新為1,改為的結果直接將相加后的值模上10的計算結果頭插進去即可。
?三目運算符十分巧妙,在求和時,要一直將兩數組的元素全部加一遍,如果短的數組已經加完就作為0,這樣也不會出現越界訪問,還解決了要判斷是哪個數組更短的問題,統一對待。在每次對應位計算完成之后更新add(進位數)。然后繼續加進位的數字。
信心滿滿提交
?最后一次進位的情況被我們忽略了,就像上邊的錯誤用例一樣。所以當i和j全部減到零,并且進位add也等于0時循環結束。
class Solution {
public:string addStrings(string num1, string num2) {int i = num1.length() - 1, j = num2.length() - 1, add = 0;string ans = "";while (i >= 0 || j >= 0 || add != 0) {int x = i >= 0 ? num1[i] - '0' : 0;int y = j >= 0 ? num2[j] - '0' : 0;int result = x + y + add;//該位置上兩個數字求和結果ans.insert(ans.begin(),'0' + result % 10);add = result / 10;i -= 1;//向前一位進發j -= 1;}return ans;}
};
這道題就完成了。
就算兩個字符串很長很長,計算也很快,結果也是準確的。
用數組進行存儲運算
?用數組存儲的話,要保證數組中任意元素不大于10,這才是無誤的存儲方法,如果某個元素大于10,那就全亂了,一般請況下我們都是用數組進行存儲大數操作,而不是計算。就算是要計算的話還要給你提供每個元素都是小于10的數字,用上邊字符串的思路進行計算就可以了。
存儲大數,一般是通過數組保存運算后很大的數字。
例如求某數的階乘
100的階乘long long就已經受不了了,那么如何進行精確計算呢?
還是回憶小學時候我們如何計算乘法的呢?
?上邊的字符串相加的題目中,我們可以發現進位add只有兩種,0或者1,因為就算兩個9相加,結果也還是18,進位仍然是1,在這里就不同了。
?我們可以把99*99看做在數組中保存的兩個9分別乘以99,他們在數組中的位置表示他們的位數。
?如果是求階乘的話,就是要將兩個數字相乘之后的數字再乘以一個數而已,同樣的思路,多了一層循環。
?就像上邊,再乘以98的話,就是數組中每一位的數字都乘以98,然后重復上邊的操作就可以了。在乘以下一個數之前,要知道上一次乘完的結果的位數,起始時可以將計算的第一個數字乘以1,然后判斷出位數,再次運算時就使數組中每一位的數字乘以要乘以的數字。
?例如100的階乘,讓數組中首元素為1,第一次運算完成之后數組中前三個元素就是0,0,1,有三位數字,位數為cout=3。然后乘以99,就是數組中前三個元素乘以99,前兩個數字不變,最后的數字是大于10的,在進位結束后,判斷數組下標為2(一共3位數字,最后一位下標為2)的位置是否大于10,如果大于就進位,此時需要進位一個9,同時位數cout加1,當然,這是一個循環,如果最后一位乘的數不是99而是101,就要進位兩次,第一次進位10,第二次進位1,當然位數就要加2。
有了上邊的思路就可以寫代碼了
int main()
{int num = 0;scanf("%d", &num);if (num == 0){printf("1");}int a[10000] = { 0 };a[0] = 1;int cout = 1;//分為三步處理for (int i = num; i > 0; i--){int j = 0;//cout是位數,將所有的位數都乘以i。for (; j < cout; j++){a[j] *= i;}//將每個位判斷一下,如果大于等于10,就向前進位for (int k = 0; k < cout; k++){if (a[k] >= 10){a[k + 1] += a[k] / 10;a[k] = a[k] % 10;}}//從最后一位進行判斷,找到新的位數for (int l = cout; a[l] >0; l++){cout++;a[l + 1] = a[l] / 10;a[l] = a[l] % 10;} }for (int i = cout-1; i >0; i--)//從后向前遍歷打印{printf("%d ", a[i]);}return 0;
}
?在循環結束時的cout就是結果的位數,同是數組中存放的就是結果的逆序。我在里開的空間是10000,如果計算出的數位數更大的話可以擴寬數組大小。
?本文到這里就結束啦,有用的話留一個贊再走叭,如果你的慧眼金睛發現了問題,一定要說哦,我會虛心改正噠。