大整數的乘法運算-c語言版**)
在計算機中,長整型(long int)變數的範圍是 -2147483648 至 2147483647,因此若用長整型變數做乘法運算,乘積最多不能超過 10位數。即便用雙精度型(double)變數,也僅能保證 16 位有效數字的精度。在某些需要更高精度的乘法運算的場合,需要用別的辦法來實現乘法運算。
比較容易想到的是做多位數乘法時列豎式進行計算的方法,只要寫出模擬這一過程的程式,就能實現任意大整數的乘法運算。經過查閱資料,找到一種更易於程式設計的方法,即「列表法」。
下面先介紹「列表法」:
例如當計算8765 x 234時,把乘數與被乘數照如下列出,見表1:
8 7 6 5 8765
2 16 14 12 10 2
3 24 21 18 15 3
4 32 28 24 20 4
表 1 表 2
把每個空格所在的行列的數字的乘積填入空格中,得表2。把錶2中的數按圖示斜線分組(橫縱座標和相等的數分為一組),把每組數的累加起來所得的和記在**下方,見表 3:
16 14 12 10
24 21 18 15
32 28 24 20
16 38 65 56 39 20
表 3
最後把**下方一行數作如下處理,見表4:
從最低位的 20 開始,保留個位數字「0」,把個位以外的數「2」進到前一位;把次低位的 39 加上低位進上來的 2 得 41,保留個位數字「1」,把「4」進到前一位;以此類推,直至最高位的 16,16 加上低位進上來的4得 20,保留「0」,把2進到最高位,得乘積答數 2051010。
16 38 65 56 39 20
2 16+4=20 38+7=45 65+6=71 56+4=60 39+2=41
留0進2 留5進4 留1進7 留0進6 留1進4 留0進2
2 0 5 1 0 1 0
表 4
根據以上思路就可以編寫c 程式了,再經分析可得:
1、乙個m 位的整數與乙個 n 位的整數相乘,乘積為m+n-1 位或m+n 位。
2、程式中,用三個字元陣列分別儲存乘數、被乘數與乘積。由第 1 點分析知,存放乘積的字元陣列
的長度應不小於存放乘數與被乘數的兩個陣列的長度之和。
3、可以把第二步「計算填表」與第三四步「累加進製」放在一起完成,可以節省儲存** 2所需的空間。
4、程式關鍵部分是兩層迴圈,內層迴圈累計一組數的和,外層迴圈處理保留的數字與進製。
編寫的程式如下:
程式 1 清單:
/* multiply.c */
/* 11/20/2008 */
#define maxlength 1000
#include
#include
void compute(char *a, char *b, char *c);
void main(void)
void compute(char *a, char *b, char *c)
if ((c[0] = carry+'0') == '0') /* if no carry, */
c[0] = '/040'; /* c[0] equals to space */
}
效率分析:用以上演算法計算 m位整數乘以n 位整數,需要先進行 m x n次乘法運算,再進行約 m
+ n次加法運算和 m + n次取模運算(實為整數除法)。把這個程式稍加修改,讓它自己產生乘數與被乘
數,然後計算隨機的 7200位整數互乘,在cyrix 6x86 pr166機器的純dos方式下耗時 7秒(用borland c
3.1編譯)。
經過改進,此演算法效率可以提高約9 倍。
注意到以下事實:8216547 x 96785 將兩數從個位起,每 3位分為節,列出乘法表,將斜線間的
數字相加;
8 216 547
96 785
768 20736 52512
6280 169560 429395
768 27016 222072 429395
將表中最後一行進行如下處理:從個位數開始,每乙個方格裡只保留三位數字,超出 1000 的部
分進製到前乙個方格裡;
768 27016 222072 429395
768+27 27016+222 222072+429
=795 =27238 =222501 395
795 238 501 395
所以8216547 x 96785 = 795238501395
也就是說我們在計算生成這個二維表時,不必一位一位地乘,而可以三位三位地乘;在累加時也是滿1000進製。這樣,我們在計算 m位整數乘以 n位整數,只需要進行 m x n / 9次乘法運算,再進行約(m + n) / 3次加法運算和(m + n) /3 次取模運算。總體看來,效率約是前一種演算法的 9倍。
有人可能會想:既然能夠三位三位地乘,為什麼不4位 4位甚至5位5位地乘呢?那不是可以提高 16 乃至 25 倍效率嗎?聽我解來:本演算法在累加表中斜線間的數字時,如果用無符號長整數(範圍 0至~4294967295)作為累加變數,在最不利的情況下(兩個乘數的所有數字均是 9),能夠累加約4294967295/(999*999)=4300 次,也就是能夠準確計算任意兩個均不超過 12900(每次累加的結果"值"三位,故 4300*3=12900)位的整數相乘。如果 4 位 4 位地乘,在最不利的情況下,能夠累加約4294967295/(9999*9999)=43 次,僅能夠確保任意兩個不超過 172 位的整數相乘,沒有什麼實用價值,更不要說5位了。
請看改進後的演算法的例項程式:
該程式隨機產生兩個72xx位的整數,把乘數與積儲存在 result.txt中。
在borland c++ 3.1 中用
bcc -3 -o2 -g -mh -z -f287 -pr -t- dashu.cpp
編譯生成的exe檔案在cyrix 6x86 pr166的機器上執行耗時0.82 秒。
程式 2 清單:
#include
#include
#include
#include
#include
#define n 7200 //作 72xx 位的整數乘法
int max(int,int,int);
int initarray(int a);
void write(int a,int l);
file *fp;
void main()
,b[5000]=,k[10001]=; //宣告存放乘數、被乘數與積的陣列
clock_t start, end; //宣告用於計時的變數
unsigned long c,d,e; //宣告作累加用的無符號長整數變數
int i,j,la,lb,ma,mi,p,q,t; //宣告其它變數
randomize(); //初始化隨機數
la=initarray(a); //產生被乘數,並返回其長度
lb=initarray(b); //產生乘數,並返回其長度
if(lala)?lb:la;
for (q=0;q=0;i--) //累加斜線間的數,i 為橫縱座標之和
e=k[0]+1000*d; //求出乘積的最高位
end = clock();//停止計時
fp = fopen("result.txt", "w+"); //儲存結果到 result.txt
printf("/nthe elapsed time was: %3.4f/n", (end - start) / clk_tck);
//列印消耗的時間
fprintf(fp,"%d",a[0]); //列印被乘數最高位
write(a,la); //列印被乘數其他位
fprintf(fp,"%d",b[0]); //列印乘數最高位
write(b,lb); //列印乘數其他位
fprintf(fp,"%ld",e); //列印乘積最高位
write(k,la+lb-1); //列印乘積其他位
fclose(fp);
}
max(int a,int b,int c)
int initarray(int a)
{
int q,p,i;
q=n+random(100);
if(q%3==0)
p=q/3;
else
p=q/3+1;
for(i=0;i
效果比較好的細化演算法
void cvthin cv mat src,cv mat dst,int intera 非原地操作時候,copy src到dst if dst.data src.data int i,j,n int width,height width src.cols 1 之所以減1,是方便處理8鄰域,防止越界...
AsyncTask 比較好的解釋
package com.example.asynctask import android.os.asynctask import android.widget.progressbar import android.widget.textview 生成該類的物件,並呼叫execute方法之後 首先執行...
ballmanford 比較好的理解方式
本文 假設存在最短路徑的話,那麼我們只要將這條最短路徑沿著權值為負的環路在繞一圈,那麼這條最短路徑的權值就會減少了,所以不存在最短的路徑,因為路徑的最小值為負無窮 一般形式 typedef struct edge edge n bool bellman ford 如果某次迴圈,沒有更新源點到任何頂點...