递归算法之不用乘号的乘法——用位移实现乘法(dart语言实现)
前兩天突發奇想,寫一個乘法的實現,但不用乘號*。并測試一下性能如何。因此就有了下面的代碼:(本文主要目的是為了玩遞歸和位移,因此僅限自然數)
首先,標準乘法:
int commonMultiplication(int a, int b) => a * b;
第二,從數學的角度,乘法其實就是加法,只是加法的簡寫而已,因此 a * b 可以理解為 b 個 a 相加;故得出用加法代替的乘法。為了減少加法的次數,取 a, b 兩數的最小值進行循環:
int plusMultiplication(int a, int b) {
var t = a > b ? b : a, r = 0;
if (t == a) {
a = b;
b = t;
}
while (b-- > 0) r += a;
return r;
}
最后,位移乘法。計算機進行位移操作的速度是非??斓模虼耍绻馨殉朔〒Q算成位移操作的話,速度會不會更快?但是根據計算機二進制的特點,每次向左位移,相當于是乘了2的n次方,怎么辦?我們看看例子:
31 * 18
怎么用位移來實現呢?首先我們取小的數:18,進行拆解。拆解的算法是,任何一個自然數,在計算機中都可以用二進制表示,那么,任何一個自然數,也可以拆解為2的冪的和。比如 18 的二進制表示 為 10010,拆解成十進制,即為 18 = 16 + 2;那么上面可以拆解為:
31 * 18 = 31 * (16 + 2) = 31 * 16 + 31 * 2
此時即可換成位移的算法:
31 * 18 = 31 * (16 + 2) = 31 * 16 + 31 * 2 = 31 * 2^4 + 31 * 2^1 = (31 << 4) + (31 << 1)
是不是轉成了位移?這里要注意對乘數 18 的處理,位移的關鍵是找到乘數 b 如何拆解為 2 的冪,這里要遞歸拆解,以18為例,第一次的關鍵是找到16這個2的冪,然后用18 - 16,結果等于 2,繼續找2等于多少2的冪,直到差小于2為止。同時,考慮到a*b,在實際計算中即使小的乘數也很大的情況,采用類似二分搜索的方式,給定階數,從大的階數向小的階數遞歸。階數的設定可以參考計算機的位數,即32位還是64位。因此,計算一個數可以拆分出來的最大2的冪的代碼如下:
int _bitShift(int d, int order) {
if (order == 0) return 0;
var i = 0;
for (var t = d; (t >>= order) > 0; d = t, t = d) i += order;
return i + _bitShift(d, order >> 1);
}
位移乘法實現的代碼
int bitMultiplication(int a, int b, int order) {
var t = a > b ? b : a, r = 0;
if (t == a) {
a = b;
b = t;
}
for (var i = _bitShift(b, order); i > 0; i = _bitShift(b, order)) {
r += a << i;
b -= 1 << i;
}
return b == 1 ? r + a : r;
}
經過采用隨機大數進行多次循環測試,結果顯示,用加法實現的乘法速度最慢,最快的仍然是 普通乘法,即用乘號的乘法;位移乘法居中??磥淼讓拥挠嬎銠C指令還是對*乘法做了優化。
最后說明一下,本文純屬原創,沒有使用dart 自帶math包,尤其是在求一個數可以拆出來的最大的2的冪的數字時。否則直接用math包中的log函數即可得出結果,但這樣就起不到自己練習位運算和遞歸的目的了。
總結
以上是生活随笔為你收集整理的递归算法之不用乘号的乘法——用位移实现乘法(dart语言实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go语言游戏服务端开发(三)——服务机制
- 下一篇: a = a + b 与 a += b 的