长整数的乘法运算
概述
都知道, 計(jì)算機(jī)中存儲(chǔ)整數(shù)是存在著位數(shù)限制的, 所以如果需要計(jì)算100位的數(shù)字相乘, 因?yàn)榫幊瘫旧硎遣恢С执鎯?chǔ)這么大數(shù)字的, 所以就需要自己實(shí)現(xiàn), 當(dāng)然了, 各個(gè)編程語(yǔ)言都有大數(shù)的工具包, 何必重復(fù)造輪子, 但我還是忍不住好奇他們是如何實(shí)現(xiàn)的, 雖然最終沒(méi)有翻到他們的底層源碼去, 但查詢(xún)的路上還是讓我大吃一驚, 來(lái)吧, 跟我一起顛覆你的小學(xué)數(shù)學(xué).
長(zhǎng)乘運(yùn)算
當(dāng)然, 如果自己實(shí)現(xiàn)這樣一個(gè)大數(shù), 用數(shù)組來(lái)存儲(chǔ)每一位是我當(dāng)前想到的方法. 那如何進(jìn)行乘法運(yùn)算呢? 因?yàn)橛脭?shù)組來(lái)存儲(chǔ)數(shù)字, 那么數(shù)字的加法也要采用每一位進(jìn)位的方式來(lái)進(jìn)行, 所以下面為了方便說(shuō)明算法的效率, 以一次個(gè)位數(shù)的運(yùn)算視為一個(gè)運(yùn)算單位.
上小學(xué)知識(shí):
- 4?5=204*5=204?5=20
- 個(gè)位數(shù)相乘, 一次運(yùn)算
- 14?5=(4?5)+(1?5)?10=7014*5=(4*5)+(1*5)*10=7014?5=(4?5)+(1?5)?10=70
- 2位數(shù)乘1位數(shù), 分解后共: 2次乘法和2位數(shù)的加法, 4次運(yùn)算(乘10可看做移位操作)
- 134?6=(4?6)+(3?6)?10+(1?6)?100=804134*6=(4*6)+(3*6)*10+(1*6)*100=804134?6=(4?6)+(3?6)?10+(1?6)?100=804
- 3位數(shù)乘1位數(shù), 分解后共: 3次乘法, 3位數(shù)的加法(不要看兩個(gè)加號(hào), 可以乘法運(yùn)算完后做連加運(yùn)算, 當(dāng)然, 也可能連加之后發(fā)生溢出, 暫不考慮. 此處簡(jiǎn)化只看加法的位數(shù)即可), 6次運(yùn)算.
- 1234?7=(4?7)+(3?7)?10+(2?7)?100+(1?7)?1000=86381234*7 = (4*7) + (3*7)*10 + (2*7)*100 + (1*7)*1000 = 86381234?7=(4?7)+(3?7)?10+(2?7)?100+(1?7)?1000=8638
- 4位數(shù)乘1位數(shù), 8次運(yùn)算.
通過(guò)上面可總結(jié)規(guī)律, n位數(shù)乘一位數(shù), 需要 2n 次運(yùn)算. 將 n 位數(shù)乘1位數(shù)的運(yùn)算稱(chēng)作短乘. 然后下面再看一下 n 位數(shù)乘 n 位數(shù).
- 14?13=(14?3)+(14?1)?10=18214*13=(14*3) + (14*1)*10=18214?13=(14?3)+(14?1)?10=182
- 兩位數(shù)相乘, 2次短乘, 4位數(shù)加法(99*9*10 最差情況). 共: 2?(2n)+4=122*(2n) + 4 = 122?(2n)+4=12 次運(yùn)算
- 132?256=(132?6)+(132?5)?10+(132?2)?100=33792132*256=(132*6)+(132*5)*10+(132*2)*100=33792132?256=(132?6)+(132?5)?10+(132?2)?100=33792
- 三位數(shù)相乘: 3次短乘, 6位數(shù)加法(最差情況), 共: 3?(2n)+6=243*(2n) + 6=243?(2n)+6=24次運(yùn)算.
通過(guò)上面, 總結(jié)規(guī)律, n位數(shù)相乘(長(zhǎng)乘)的運(yùn)算次數(shù)是: n?(2n)+2n=2n2+2nn*(2n) + 2n = 2n^2+2nn?(2n)+2n=2n2+2n 次運(yùn)算. 當(dāng)然, 這就是我們從小接受的進(jìn)行乘法運(yùn)算的方法, 所以寫(xiě)成這樣還好, 比較合乎常理. 時(shí)間復(fù)雜度是 O(n^2)
但是, 他還可以更快么? 我以為就這樣了, 是我小看了偉大的數(shù)學(xué)家. .
Karatsuba方法
由簡(jiǎn)入難, 先看一下兩位數(shù)的乘法:
12*34, 為了方便初中方程未知數(shù)的思維, 我們將這兩個(gè)數(shù)字拆解一下:
KaTeX parse error: No such environment: align* at position 8: \begin{?a?l?i?g?n?*?}? 12 &= 10a+b (其…
則,
KaTeX parse error: No such environment: align* at position 8: \begin{?a?l?i?g?n?*?}? & 12*34 \\ =& …
當(dāng)化簡(jiǎn)到這里, 2位數(shù)相乘需要幾次運(yùn)算? 來(lái)算一下:
- 10(am+bn)10(am + bn)10(am+bn) : 共2次乘法, 2位數(shù)加法, 共4次運(yùn)算.
- an 和 bm : 共2次乘法, 共2次運(yùn)算
- 剩下最外層的加法, 最差情況: (100?9?9100*9*9100?9?9 4位數(shù), 10?(9?9+9?9)10*(9*9 + 9*9)10?(9?9+9?9) 4位數(shù)), 共4次運(yùn)算
- 則總計(jì), 4+4+2=104+4+2=104+4+2=10次運(yùn)算.
此時(shí), 需要的運(yùn)算次數(shù)已經(jīng)較之前的12次少一些了, 但是別急, 容我把公式再變換一下.
令:
u=anw=bms=(b?a)?(m?n)u=an \\ w=bm \\ s=(b-a)*(m-n) u=anw=bms=(b?a)?(m?n)
公式:
100u+(u+w?s)?10+w=100an+(an+bm?(b?a)?(m?n))?10+bm=100an+(an+bm?bm+bn+am?an)?10+bm=100an+(bn+am)?10+bm100u + (u+w-s)*10+w \\ = 100an + (an + bm - (b-a) * (m-n)) *10 + bm \\ = 100an + (an + bm - bm + bn + am - an)*10 + bm \\ = 100an + (bn + am) * 10 + bm 100u+(u+w?s)?10+w=100an+(an+bm?(b?a)?(m?n))?10+bm=100an+(an+bm?bm+bn+am?an)?10+bm=100an+(bn+am)?10+bm
是不是和上面的公式一樣了呢? 是的, 那轉(zhuǎn)換公式是為了什么呢? 當(dāng)然是為了減少運(yùn)算次數(shù)啦. 算一下:
- 計(jì)算u : 1次運(yùn)算
- 計(jì)算w: 1次運(yùn)算
- 計(jì)算 s: 3次運(yùn)算
- 計(jì)算 u+w-s: 2位數(shù)運(yùn)算, 2次運(yùn)算
- 計(jì)算最外層加法: 3位數(shù)運(yùn)算, 3次運(yùn)算
- 共: 10次運(yùn)算.
這和我剛才計(jì)算的不也是10次么? 不過(guò)個(gè)位數(shù)的乘法換成加法就會(huì)變快了么? 不要小看這個(gè)一次乘法運(yùn)算的減少, 從上面能夠看出, 乘法運(yùn)算的運(yùn)算次數(shù)是隨位數(shù)成指數(shù)增長(zhǎng)的, 而加法運(yùn)算則隨位數(shù)成線性增長(zhǎng), 等看了下面的多位數(shù)相乘, 你就知道減少的這一次乘法運(yùn)算有什么用了.
不過(guò)下面才是重頭戲, 數(shù)字多了之后, 此算法就明顯比傳統(tǒng)的快的多了.
4位數(shù)乘法
計(jì)算: 1234?56781234*56781234?5678
設(shè):
1234=100a+b(其中a=12,b=34)5678=100n+m(其中n=56,m=78)1234+5678=(100a+b)?(100n+m)1234=100a+b (其中 a=12, b=34) \\ 5678=100n+m (其中 n=56, m=78) \\ 1234+5678 = (100a + b) * (100n + m) 1234=100a+b(其中a=12,b=34)5678=100n+m(其中n=56,m=78)1234+5678=(100a+b)?(100n+m)
套用上面的公式:
令:
u=anw=bms=(b?a)?(m?n)u=an \\ w=bm \\ s=(b-a)*(m-n) u=anw=bms=(b?a)?(m?n)
則結(jié)果為: 10000u+(u+w?s)?100+w10000u + (u+w-s)*100+w10000u+(u+w?s)?100+w
此次進(jìn)行了幾次運(yùn)算呢? 算一下:
- 計(jì)算 u: 兩位數(shù)乘法, 10次運(yùn)算
- 計(jì)算w: 10次運(yùn)算
- 計(jì)算s: 兩位數(shù)減法兩次, 一次乘法, 14次運(yùn)算
- 計(jì)算整體: 8位數(shù)相加(99?99?1000099*99*1000099?99?10000), 8次運(yùn)算
- 整體: 10+10+14+8=3210+10+14+8=3210+10+14+8=32次運(yùn)算.
32次運(yùn)算, 之前長(zhǎng)乘的方式需要幾次呢? 2?(4?4)+2?4=402*(4*4) + 2*4=402?(4?4)+2?4=40. 是不是少了.
也就是說(shuō), 4位數(shù)的乘法, 其中用到了3次兩位數(shù)乘法, 2次兩位數(shù)減法, 1次8位數(shù)加法.
8位數(shù)乘法
8位數(shù)乘法就不展開(kāi)了, 直接套用4位數(shù)乘法得出的結(jié)論, 其運(yùn)算次數(shù)為:
- 3次4位數(shù)乘法: 3?32=963*32=963?32=96次
- 2次4位數(shù)減法: 2?4=82*4=82?4=8次
- 1次 9999?9999?1000000009999*9999*1000000009999?9999?100000000 位數(shù)加法: 17次
- 共: 96+8+17=12196+8+17=12196+8+17=121次運(yùn)算.
原來(lái)的長(zhǎng)乘需要幾次呢? 2?(8?8)+2?8=1442*(8*8) + 2*8=1442?(8?8)+2?8=144次.
是不是有一種動(dòng)態(tài)規(guī)劃, 分而治之的感覺(jué)? 可以利用函數(shù)遞歸來(lái)實(shí)現(xiàn).
問(wèn)題
想必此算法的問(wèn)題也很明顯了, 為了每次都能將數(shù)字拆成左右兩部分, 所以只能夠計(jì)算位數(shù)是2的 n 次方的數(shù)字, 如果位數(shù)不足, 則需要在前邊進(jìn)行補(bǔ)0.
算法比較
為了比較兩個(gè)算法的運(yùn)算次數(shù), 讓我們忽略運(yùn)算的低次冪以及常數(shù)項(xiàng), 則(以下 n 為2的冪):
長(zhǎng)乘
f(n)={1,n==?12?(2n)2,elsef(n) = \begin{cases} 1, \text{ $n$ == 1} \\ 2 * (2^n)^2, \text{else} \end{cases} f(n)={1,?n?==?12?(2n)2,else?
Karatsuba:
f(n)={3,n==13?f(n?1),elsef(n) = \begin{cases} 3, \text{$n$==1} \\ 3*f(n-1), \text{else} \end{cases} f(n)={3,n==13?f(n?1),else?
分別進(jìn)行計(jì)算:
| 20=12^0=120=1 | 1 | 1 |
| 21=22^1 = 221=2 | 8 | 3 |
| 210=10242^{10}=1024210=1024 | 2097152 | |
| 220=10485762^{20}=1048576220=1048576 | 2199023255552 | 1162261467 |
| 250=11258999068426242^{50}=1125899906842624250=1125899906842624 | 2535301200456458802993406410752 | 239299329230617529590083 |
| 2100=12676506002282294014967032053762^{100}=12676506002282294014967032053762100=1267650600228229401496703205376 | 3213876088517980551083924184682325205044405987565585670602752 | 171792506910670443678820376588540424234035840667 |
可以看出來(lái), 當(dāng)數(shù)字的位數(shù)越大, 則兩個(gè)算法之間的差距越明顯.
有沒(méi)有被顛覆的感覺(jué)? 是不是自己知道了20多年的乘法運(yùn)算, 根本沒(méi)有想到還有其他計(jì)算乘法的運(yùn)算規(guī)則? 我也沒(méi)想到, 漲見(jiàn)識(shí)了…
果然, 沒(méi)有什么是偉大的科學(xué)家們做不到的, 這算法我看了近乎整整一天, 草稿紙廢了四十張, 總算是略知一二了.
總結(jié)
- 上一篇: linux系统建立文件系统,linux文
- 下一篇: NS2:undefined refere