大整数的四则运算
對一道A+B的題目,如果A和B的范圍在int范圍內,那么相信大家很快就能寫出程序。但是如果A和B是有著1000個數位的整數,恐怕就沒有辦法用已有的數據類型來表示
這時就只能老實去模擬加減乘除的過程。怎么樣?聽起來像是小學生學的東西吧?實際上原理就是小學的,所以不要去害怕這個看上去很高深的東西。此外,大整數又稱為高精度整數,其含義就是用基本數據類型無法存儲其精度的整數。
目錄
- 大整數的存儲
- 大整數的加法
- 大整數的減法
- 大整數的乘法
- 大整數的除法
大整數的存儲
很簡單,使用數組即可。例如定義int型數組d[1000],那么這個數組中的每一位就代表了存放的整數的每一位。如將整數235813存儲到數組中,則有d[0] =3, d[1]= 1, d[2]=8, d[3] =5, d[4] =3, d[5]=2,即整數的高位存儲在數組的高位,整數的低位存儲在數組的低位。不反過來存儲的原因是,在進行運算的時候都是從整數的低位到高位進行枚舉,順位存儲和這種思維相合。但是也會由此產生一個需要注意的問題:把整數按字符串%s讀入的時候,實際上是逆位存儲的,即str[0] =‘2, st[]=’,… str[5]=3,因此在讀入之后需要在另存為至d[]數組的時候反轉一下。
而為了方便隨時獲取大整數的長度,一般都會定義一個int型變量len來記錄其長度,并和d數組組合成結構體:
這里bign是big number
顯然,在定義結構體變量之后,需要馬上初始化結構體。為了減少在實際輸入代碼時總是忘記初始化的問題,
我們盡量在結構體里寫一個構造函數來初始化。
代碼如下:
構造函數是用來初始化結構體的函數,函數名和結構體名相同、無返回值,
因此非常好寫。大整數結構體bign就變成了這樣:
這樣在每次定義結構體變量時,都會自動對該變量進行初始化。
而輸入大整數時,一般都是先用字符串讀入,然后再把字符串另存為至bign結構體。
由于使用char數組進行讀入時,整數的高位會變成數組的低位,而整數的低位會變成數組的高位,
因此為了讓整數在bign中是順位存儲,需要讓字符串倒著賦給d[]數組:
如果要比較bign變量的大小,規則也很簡單:
先判斷兩者的len大小,
如果不相等,則以長的為大。
如果相等,則從高位到地位進行比較,直到出現某一位不等,就可以判斷兩個數的大小。
下面的代碼直接依照了這個規則:
大整數的加法
由上圖可以歸納出對其中一位進行加法的步驟:
將該位上的兩個數字與進位相加,得到的結果取個位數作為該位的結果,取十位數作為新的進位。
代碼如下:
下面是完整的A+B的代碼:
#include<cstdio> #include<cstring> struct bign {int d[1000];int len;bign(){memset(d,0,sizeof(d));len=0;} }; bign change(char str[])//將字符串轉換為bign {bign a;a.len=strlen(str);//bign的長度就是字符串的長度for(int i=0;i<a.len;i++){a.d[i]=str[a.len-i-1]-'0';//逆著賦值}return a; } int compare(bign a,bign b) {if(a.len>b.len) return 1;else if(a.len<b.len) return -1;else{for(int i=a.len-1;i>=0;i--)//從高位到低位比較{if(a.d[i]>b.d[i]) return 1;//只要有一位a大,則a大else if(a.d[i]<b.d[i]) return -1;//只要有一位a小,則a小}return 0;//兩數相等} } bign add(bign a,bign b) {bign c;int carry=0;//carry是進位for(int i=0;i<a.len||i<b.len;i++){int temp=a.d[i]+b.d[i]+carry;//兩個對應位與進位相加c.d[c.len++]=temp%10;//個位數為該位的結果carry=temp/10;//十進制為新的進位if(carry!=0){c.d[c.len++]=carry;}}return c; } void print(bign a) {for(int i=a.len-1;i>=0;i--){printf("%d",a.d[i]);} } int main(void) {char str1[1000],str2[1000];scanf("%s %s",&str1,&str2);bign a=change(str1);bign b=change(str2);print(add(a,b));return 0; }最后指出,這樣寫法的條件是兩個對象都是非負整數。
如果有一方是負的,可以在轉換到數組這一步時去掉負號,然后采用高精度減法;
如果兩個都是負的,就都去掉負號后用高精度加法,最后再把符號加回去即可。
大整數的減法
同樣可以得到一個簡練的步驟:
對某一步,比較被減位和減位,如果不夠減,則令被減位的高位減1,被減位加10再進行減法。
如果夠減,則直接減。
最后一步要注意減法后高位可能有多余的0,要去除它們,但也要保證結果至少有一位數。
代碼如下:
bign sub(bign a,bign b) {bign c;for(int i=0;i<a.len||i<b.len;i++)//以較長的為界限{if(a.d[i]<b.d[i])//如果不夠減{a.d[i+1]--;//向高位借位a.d[i]+=10;//當前位加10}c.d[c.len++]=a.d[i]-b.d[i];//減法結果為當前位}while(c.len-1>=1&&c.d[c.len-1]==0){c.len--;//去除高位的0,同時至少保留一位最低位}return c; }最后需要指出的是,使用sub函數前要比較兩個數的大小,如果被減數小于減數,
需要交換兩個變量,然后輸出負號,再使用sub函數。
大整數的乘法
bign nulti(bign a,int b) {bign c;int carry=0;//進位for(int i=0;i<a.len;i++){int temp=a.d[i]*b+carry;c.d[c.len++]=temp%10;//個位做為該位結果carry=temp/10;//高位部分作為新的進位}while(carry!=0)//和加法不一樣,乘法的進位可能不止一位{c.d[c.len++]=carry%10;carry/=10;}return c; }需要注意的是如果a和b中存在負數,需要先記錄下來其負號,然后取它們的絕對值帶入函數。
大整數的除法
bign divide(bign a,int b,int& r)//高精度除法,r為余數 {bign c;c.len=a.len;//被除數的每一位和商的每一位都是一 一對應的,因此先令長度相等for(int i=a.len-1;i>=0;i--)//從高位開始{r=r*10+a.d[i];//和上一位遺留的余數組合if(r<b) c.d[i]=0;//不夠除,該位為0else//夠除{c.d[i]=r/b;//商r=r%b;//獲得新的余數}}while(c.len-1>=1&&c.d[c.len-1]==0){c.len--;//去除高位的0,同時至少保留一位最低位}return c; }在上述代碼中,考慮到函數每次只能返回一個數據,而很多題目里面會經常要求得到余數,
因此把余數寫成"引用"的形式直接作為參數傳入,或是把r設成全局變量。
用了引用,其作用是在函數中可以視作直接對原變量進行修改。
總結
- 上一篇: 质因子分解习题
- 下一篇: vector相关习题