大整數的乘法 比較好的演算法!

2021-04-30 07:54:29 字數 4490 閱讀 3590

大整數的乘法運算-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 如果某次迴圈,沒有更新源點到任何頂點...