大数除法(超长整数运算除法器)详解
在大數運算中,比較難實現的應該是高精度/高精度的除法器。
目錄
一、原理
二、具體代碼解析
三、超長整數運算
一、原理
1.大數存儲
????????先說說大數在C語言程序中是怎么存儲的。我們使用長度為N的int數組來存儲無符號超長整數,其中數組每個元素存儲的值上限為M。如下:???????
#define M 10000 //M進制,int數組每個元素取值上限 #define N 5 //數組長度 int x[N]={0};?????????因為int類型最多表示10位有效數字(最大值為2147483648),當兩個有效位數為5的int類型變量相乘時其結果可能溢出,為了便于乘法器的實現M取值最好小于10000?
????????舉個例子,當我們要用x存儲一個無符號超長整數6543210435135314時,數組x的值是這樣的:x[0]=5314,x[1]=3513,x[2]=2104,x[3]=6543,x[4]=0。
2.除法算法
除法操作數的關系如下:
????????被除數/除數=商......余數,其中被除數=商*除數+余數
首先除數不能為0;
當被除數為0時,商為0;
當被除數小于除數時,商為0,余數為被除數;
當被除數等于除數時,商為1,余數為0;
?……
1)日常生活中,我們是這樣算除法的:
?
2)但是在大數除法中,我們不能直接這么算。例如:對于1234 5678 9000 / 1 2345,計算機沒法直接計算1234 5678 / 1 2345的結果。
3)在計算機中,減法的實現要比除法簡單得多,因此我們考慮把除法轉變成減法來計算。例如:對于7894/32,我們有:
?
通過被除數不斷地減去除數,我們得到了余數22,而除數被減的次數就是商246。?
4)現在我們已經知道了如何在計算機中把除法轉變成減法來運算,但一個除數一個除數的減實在太慢了,那么有沒有更快的算法呢?答案是肯定的,我們可以通過移位來加快“減”的速度,例如:
?
?如圖,7894=32*2*100+32*4*10+32*6*1+22,一共減去了2*100+4*10+6*1=246個32,所以7894/32的商為246。
二、具體代碼解析
1.首先函數原型如下:
#define M 10000 //M進制,int數組每個元素取值上限 #define N 5 //數組長度 int div2(int n1[],int n2[],int quotient[]); /* 大數除法:無符號高精度整數/無符號高精度整數,n1為被除數,n2為除數,quotient為商。函數正確執行時返回整數1 */此外還涉及到這些函數:?
int add(int num1[],int num2[],int sum[]); /* 大數加法 */ int sub(int num1[],int num2[],int difference[]); /* 大數減法 */ int mul(int num1[],int num2[],int product[]); /* 大數乘法 */ int div(int num1[],int num2,int quotient[]); /* 大數除法:無符號高精度整數/無符號低精度整數,其中num2>0且num2<=M */ int cmp(int num1[],int num2[]); /* 大數比較,num1>num2時return1,小于時返回-1,等于時返回0 */?2.異常值處理
????????因為我們這里存儲的是無符號超長整數,參與運算的操作數也應該是無符號,并且數組的每個元素應該小于M。所以有:?
int div2(int n1[],int n2[],int quotient[]){ int i=0; int num1[N]={0},num2[N]={0};for(i=0;i<N;i++){ //異常值處理if(n1[i]<0||n2[i]<0||n1[i]>=M||n2[i]>=M){printf("div2():Operand are forbidden!\n");return -2;} //初始化:num1[i]=n1[i];num2[i]=n2[i];quotient[i]=0; } return 1;//運算正確 }????????這里說明一下為什么函數不直接用n1、n2參與運算,這是因為這個函數div2傳遞的參數都是指針,而接下來的算法將會改變被除數的值。為防止函數結束后被除數的值被改變,所以新定義了和n1、n2值相等的兩個局部變量num1、num2。
3.特殊值處理
????????包含以下特殊情況的處理:?(1)首先除數不能為0;(2)當被除數為0時,商為0;(3)當被除數小于除數時,商為0;(4)當被除數等于除數時,商為1。
/*----特殊值處理----*///檢查除數是否為0for(i=N-1;i>=0&&(num2[i]==0);i--);//從高位開始尋找num2首個不為0的下標if(i<0){printf("除數不能為0\n");return -1;}//被除數小于除數if((flag=cmp(num1,num2))==-1){quotient[0]=0;return 1;}//被除數等于除數else if(flag==0){quotient[0]=1;return 1;} /*---特殊值處理end---*/?//別忘了在前面聲明flag!
4.處理完上述特殊情況,剩下的就是被除數大于除數的情況了,從這里開始就是算法的核心部分。
????????首先,為了加快減的速度,我們要盡可能地把除數左移。因為M取值10000,num2[i]有效位數為4,我們先4位(十進制位)4位(十進制位)的左移,直到除數num2大于被除數num1時停止。接下來把除數1位(十進制位)1位(十進制位)地右移,直到num1>num2時停止。這時,被除數num1剛好大于除數num2。?
/*---計算除數最大移位---*/if(num2[N-1]>0)flag=0;//flag=0表示除數不能移位,因為最高位>0for(counts=0;counts<N&&(flag>0);){//num2左移4位:for(i=N-1;i>0;i--)num2[i]=num2[i-1];num2[0]=0;counts++;//左移次數+4,counts+1flag=cmp(num1,num2);}counts=counts*4;for(i=0;i<counts&&(cmp(num1,num2)<0);i++){if(div(num2,10,num2)!=1) return -1;//如果除數num2大于被除數num1,右移1位}counts=counts-i;//此時counts為除數左移位數 /*--計算除數最大移位end--*///這里左移一個10進制位是用高精度整數/低精度整數除法div實現的
//counts為num2左移位數(十進制位),別忘了在前面聲明!
5.計算被除數減去除數的次數
for(;counts>=0;counts--){if(cmp(num1,num2)>=0){//除數num2左移counts位時,num1最多能減去num2的次數times:for(times=0;times<M&&(cmp(num1,num2)>=0);times++){sub(num1,num2,num1);}//除數num2左移counts位時,被除數最多能減去除數t=times*(10^counts)次:for(i=0;i<N;i++){t[i]=0;m[i]=0;}//初始化t、mt[0]=times;m[0]=10;for(i=0;i<counts;i++){if(mul(t,m,t)!=1)return -1;}//“減”的次數:if(add(quotient,t,quotient)!=1) return -1;}if(div(num2,10,num2)!=1) return -1;//除數右移1位 }?//別忘了在前面聲明times、t、m!
6.完整的div2函數定義如下:
#define M 10000 //M進制,int數組每個元素取值上限 #define N 5 //數組長度 //函數聲明: int add(int num1[],int num2[],int sum[]); int cmp(int num1[],int num2[]); int sub(int num1[],int num2[],int difference[]); int mul(int num1[],int num2[],int product[]); int div(int num1[],int num2,int quotient[]); /* ===無符號超長整數 除法=== quotient=n1/n2 高精度整數/高精度整數 */ int div2(int n1[],int n2[],int quotient[]){int i=0,flag=1,counts=0,times=0;int t[N]={0},m[N]={0},num1[N]={0},num2[N]={0};for(i=0;i<N;i++){//異常值處理if(n1[i]<0||n2[i]<0||n1[i]>=M||n2[i]>=M){printf("div2():Operand are forbidden!\n");return -2;}//初始化:num1[i]=n1[i];num2[i]=n2[i];quotient[i]=0;}/*----特殊值處理----*///檢查除數是否為0for(i=N-1;i>=0&&(num2[i]==0);i--);//從高位開始尋找num2首個不為0的下標if(i<0){printf("除數不能為0\n");return -1;}//被除數小于除數if((flag=cmp(num1,num2))==-1){quotient[0]=0;return 1;}//被除數等于除數else if(flag==0){quotient[0]=1;return 1;}/*---特殊值處理end---*//*---計算除數最大移位---*/if(num2[N-1]>0)flag=0;//flag=0表示除數不能移位,因為最高位>0for(counts=0;counts<N&&(flag>0);){//num2左移4位:for(i=N-1;i>0;i--)num2[i]=num2[i-1];num2[0]=0;counts++;//左移次數+4,counts+1flag=cmp(num1,num2);}counts=counts*4;for(i=0;i<counts&&(cmp(num1,num2)<0);i++){if(div(num2,10,num2)!=1) return -1;//如果除數num2大于被除數num1,右移1位}counts=counts-i;//此時counts為除數左移位數/*--計算除數最大移位end--*/for(;counts>=0;counts--){if(cmp(num1,num2)>=0){//除數num2左移counts位時,num1最多能減去num2的次數times:for(times=0;times<M&&(cmp(num1,num2)>=0);times++){sub(num1,num2,num1);}//除數num2左移counts位時,被除數最多能減去除數t=times*(10^counts)次:for(i=0;i<N;i++){t[i]=0;m[i]=0;}//初始化t、mt[0]=times;m[0]=10;for(i=0;i<counts;i++){if(mul(t,m,t)!=1)return -1;}//“減”的次數:if(add(quotient,t,quotient)!=1) return -1;}if(div(num2,10,num2)!=1) return -1;//除數右移1位}return 1;//運算正確 }三、超長整數運算
? ? ? ? 如果需要大數運算的完整代碼(包括輸入輸出、加減乘除函數),可以參考我的另一篇文章:超長整數運算——從斐波那契數列說起?的<無符號超長整數運算>部分。
總結
以上是生活随笔為你收集整理的大数除法(超长整数运算除法器)详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目[P2P文件下载器]
- 下一篇: 以太网(Ethenet)协议