*1. 這個程式的乘法數目還可以再降低,
其實我們保持完整的m, m^2, m^4, m^8, ...是沒多大需要的,
請以此為出發點來改良程式。
2. 請用手算方式列出計算2^13, 2^15, 2^16的過程,
是不是這個程式也會得到算2^15時要多幾個乘法呢?
請問一般而言,當n是多少時會比較慢?
3. 如果您用的硬體系統會偵測出整數溢位(Overflow),
那麼這個程式可能就會有問題,因為求出m^45時會同時多算了m^64,
那麼m^64就可能溢位。請修改程式克服這一點問題。
個人解答:
1. 這題不太確定題目的意思。
因為不論如何還是會經過m的偶數次方。
如果是指想要快速經過中間二進位的0值的話,
大概再將3.的答案改成如下:
Code:
while(1) { while( n & 0x01UL == 0 ) if((n >>= 1) > 0) m *= m; if ( n & 0x01UL == 1 ) // last bit is 1 temp *= m; if((n >>= 1) > 0) m *= m; else break; }這並不是好作法,而且會降低程式可讀性。
如果有人能理解這題到底是想改進什麼的話,
請不吝指教XD~
2. 換成二進位來看時,13 = 1101 , 15 = 1111, 16 = 10000
我們以只考慮temp*=m和m*=m這兩個乘法作的總次數為前提來看的話,
1101會做7次,1111會做8次,10000會做6次,
簡而言之,乘法的總次數相當於(二進位值的總位數+二進位值裡的1的總數)。
所以在二進位位數同等狀況下,裡面含有越多1,程式就做越慢。
這點跟之前遞迴解時,有些n會導致乘法次數變多的原因是一樣的。
3. 由於n為正整數,我們可以將判斷迴圈繼續的條件搬到迴圈裡面,
先判斷n丟掉最右邊的bit後是否還大於0,
還大於0的話才表示有繼續將m往上計算的價值,
否則的話我們就可以直接break。
Code:
while(1) { if ( n & 0x01UL == 1) // last bit is 1 temp *= m; if((n >>= 1) > 0) m *= m; else break; }
原本的程式演算法(用2的次方)並非最佳的解
回覆刪除ex. 求m^15,用你的方法要6次乘法,但:
(1) (m)*(m) ...m^2
(2) (m^2)*(m^2) ...m^4
(3) (m^2)*(m) ...m^5
(4) (m^5)*(m^5) ...m^10
(5) (m^10)*(m^5) ...m^15
五次即可
我猜第一題要問的可能跟這個有關...但是我也還沒想出實際上應該要如何改良演算法?
----------------------------------
另外第二題讓我想到另外一個加速方法,也不知道跟第一題有沒有關~
就是...如果是要求 m^(1111) 的話
可以求 m^(10000) 然後除 m
這樣乘法次數會比較少
=> 即,欲求m^x (x為二進位制),可比較x和!x的bit為1的個數,來選擇要算m^x還是m^(!x),然後除m^t,t=min二的次方s.t.大於x
(ex. 欲求m^(1111),因為bitSum(0000) < bitSum(1111),所以求m^(0000)...事實上等於m所以不用求,然後以m^(10000)/m^(0000)即得答案)
(ex2. 欲求m^(111101),則用m^(1000000)/m^(10)求之)
不過這方法要花memory紀錄m的次方值,又要用到除法,不知道有沒有在題目限制內XD,但是確定可以加速就是了
=======================================
方法一,只有idea,還沒想出演算法 (交給你了!)
方法二,不知道複雜度多少欸,不知道有降低演算法複雜度,還是只有改善程式效率而已