眾所周知,整數在C和C++中以int
,long
,long long
三種不同大小的資料儲存,資料大小最大可達2^64
,但是在實際使用中,我們仍不可避免的會遇到爆long long
的超大數運算,這個時候,就需要我們使用高精度演演算法,來實現巨大數的運算。
高精度的本質是將數位以字串的形式讀入,然後將每一位分別存放入int
陣列中,通過模擬每一位的運算過程,來實現最終的運算效果。
今天,我們先講解高精度加法的C語言實現:
但其實我這版C語言的高精度演演算法封裝是很有問題的,沒有stl,字串的操作是比較繁瑣的,以後熟悉C++後我會再寫一版簡易的,標準的高精度演演算法解析,但通過本文了解高精度的思路也是沒有問題的。
#include<stdio.h>
const int N = 100001;
int add(int a[], int b[], int c[], int len1, int len2)
{
int t = 0, i = 0, max = len1 > len2 ? len1 : len2;
//max指兩加數中較大者的位數,兩數之和c位數至少是max
//標識變數t值為0或1,代表是否進位,初始為0
for (; i <= max; i++)//運算到較大者位數後一位停止
{
c[i] = (a[i] + b[i] +t) % 10;//c的每一位為兩數該位之和加上t再模去10
t = (a[i] + b[i] + t) / 10;//若和>10,則c[i]取其個位,t取其十位
}//i遍歷至max+1
if (c[i - 1] == 1) return i;//若最高位為1,則返回c長度為max+1,即i
else return i - 1;//否則返回max,即i-1
}
int main()
{
char str1[N], str2[N];//兩個數的字串形式
int a[N] = { 0 }, b[N] = { 0 }, c[N] = { 0 };//ab為加數,c為和
char x;
int len1 = 0, len2 = 0;//兩數位數
do {
scanf("%c", &x);
str1[len1++] = x;
}while (x != '\n');
do{
scanf("%c", &x);
str2[len2++] = x;
} while (x != '\n');
len1--; len2--;//將資料讀入str1和str2,同時記錄位數
for (int i = len1 - 1; i >= 0; i--)
a[i] = str1[len1 - i - 1]-'0';
for (int i = len2 - 1; i >= 0; i--)
b[i] = str2[len2 - i - 1] - '0';//將ab的每一位轉換為整形存入陣列
int len3 = add(a, b, c, len1, len2);//執行高精度加法函數
for (int i = len3 - 1; i >= 0; i--)
printf("%d", c[i]);//輸出
return 0;
}
對大數來說,輸入便已經是一個有些麻煩的問題,無法讀取整形,只能以字串形式,而且連有幾位數位都不知道。
char str1[N], str2[N];//兩個數的字串形式
int a[N] = { 0 }, b[N] = { 0 }, c[N] = { 0 };//ab為加數,c為和
char x;
int len1 = 0, len2 = 0;//兩數位數
do {
scanf("%c", &x);
str1[len1++] = x;
}while (x != '\n');
do{
scanf("%c", &x);
str2[len2++] = x;
} while (x != '\n');
len1--; len2--;//將資料讀入str1和str2,同時記錄位數
這裡是主函數的變數宣告和輸入部分,若是程式只執行一次高精度運算,我們可以把變數的宣告放在主函數以外,來能減少函數的引數個數。
我們將讀取的字元賦值給x
,然後再放入字串陣列,最後對x
進行判斷,若x
為換行符、空格或其他標識著資料輸入結束的字元,則終止迴圈。
同時,迴圈中變化的陣列下標我們直接記為len1
和len2
,代表兩個數位的長度。
顯然,字元形式的數位並不好運算,所以,我們需要將每一位轉換為整形存入陣列,方便後續的計算。
那此時我們就會遇到一個問題,陣列的第0位應該存放最高位還是存放個位呢?先看程式碼實現:
for (int i = len1 - 1; i >= 0; i--)
a[i] = str1[len1 - i - 1]-'0';
for (int i = len2 - 1; i >= 0; i--)
b[i] = str2[len2 - i - 1] - '0';//將ab的每一位轉換為整形存入陣列
在這段函數中,我們從高位向低位,將每一位的字元-'0'
,得到他的整形,然後存入陣列,最終得到從低位到高位的新陣列。
為什麼要反過來存放呢,這就要考慮到一個最高位進位的問題。
陣列後面存放最高位,在最高位進位時顯然比最高位放在第0位元運算起來更方便,前者只需要在下一位+1
,而後者想要進位,可能只能依靠於額外的標記變數了。
這種問題在後面的高精度乘法中更是明顯,所以,在高精度運算中,為了使高位靈活變動,我們一般都採用倒序的存放順序,即陣列前面存低位,後面存高位。
到這裡,我們就將準備工作做完了,數位已經放入陣列,長度也已得知,這時我們就需要寫一個函數來執行高精度加法,程式碼如下:
int add(int a[], int b[], int c[], int len1, int len2)
{
int t = 0, i = 0, max = len1 > len2 ? len1 : len2;
//max指兩加數中較大者的位數,兩數之和c位數至少是max
//標識變數t值為0或1,代表是否進位,初始為0
for (; i <= max; i++)//運算到較大者位數後一位停止
{
c[i] = (a[i] + b[i] +t) % 10;//c的每一位為兩數該位之和加上t再模去10
t = (a[i] + b[i] + t) / 10;//若和>10,則c[i]取其個位,t取其十位
}//i遍歷至max+1
if (c[i - 1] == 1) return i;//若最高位為1,則返回c長度為max+1,即i
else return i - 1;//否則返回max,即i-1
}
雖然圖中解析已經非常到位了,但我還是簡單講解一下吧。
首先從i=0
位開始,將a[i]
與b[i]
和t
相加,其個位便是c
在該位的值,所以我們對他模上10
,其大於10
時需要進位,那我們就將其除以10
,整形除法下取整,得到1
或0
,作為t
的值,來參與下一位的運算。
最後,我們通過對最高位的0
或1
判斷,來決定返回max
還是max+1
。
這時,我們已經將結果存入c
了,只差輸出了,但想要輸出我們怎麼知道c
有幾位呢?最高位到底有沒有進位呢?那其實我們的函數返回值就是c
的長度了。
int len3 = add(a, b, c, len1, len2);//執行高精度加法函數
for (int i = len3 - 1; i >= 0; i--)
printf("%d", c[i]);//輸出
這樣,我們從後往前一位位輸出,就得出了最終結果了。
總而言之言而總之,高精度演演算法就是單獨將每一位數位存入陣列,分別計算,模擬我們手動計算的過程,接下來的減法和乘法除法的核心思想都是這個,那麼以上便是對高精度加法演演算法的介紹,本文由涼茶coltea撰寫,思路來自AcWing,大佬yxc的課程。