contributed by < jeremy3951
>
(2020q3)進階電腦系統理論與實作
1
填補下面的程式碼 :
失敗的算術右移 :
-3 位移完變 +14 .
為了避免這種情況發生 , 我們要讓負數右移後新的 bits 變成 1 .
運作原理 : 根據 m 的正負來決定右移後要補 0 還是 1 .
根據運作原理來觀察這行程式碼 (m >> n) | (fix ^ (fix >> n))
可以得知在 m 位移完後 , 要用 fix 來修改位移後的新 bits .
首先觀察 logical
這個變數會發現它不是 0 就是 1 , 再看到
unsigned int fixu = -(logical & (OP2))
, 如果此時把 logical=0
帶進去後 :
fixu=0
-> fix=0
-> return (m>>n)|0
( 沒有動到 m>>n )
這樣代表當 logical=0
的時候是正常狀況不用做修改
這時我們知當 logical=1
的時候就是我們要做修改的時候了 .
此時回來看第一行 (((int) -1) OP1) > 0;
若 logical=1
則 -1 經過 op1 後要 >0 , 選項 ( c )~( g ) 都會隨著 input 而有不同的正負值 , 所以可以去掉 , 選項( a ) 會讓 logical=0
, 所以 op1 的答案是 ( b ) >>1 .
根據 op1 的答案我們會發現 -1 經過算術右移後變成了正數 , 想必是用 0 來當作左邊的新 bits , 所以這時候才有修改的必要 .
接下來再看回 unsigned int fixu = -(logical & (OP2))
, 推測 fixu 的值不是 -1 * 0 就是 -1 * 1 , 這時我們看 -1 * 1 這條支線可以推得 logical & op2=1
也就是 1 & op2 =1
, 所以 op2 就是 1 根據這個答案從選項中只剩 (m>0) 和 (m<0) 可以選 , 這時回頭看我們的假設是建立在需要修改的時候做更動 , 也就是 "負數做右移的時候補0當作新的 bits" , 從這個假設可得知我們的 m 要小於 0 才符合我們的假設 , 故 op2 選 ( c )
2
題目 :
利用 __builtin_ctz 來實作 LeetCode 342. Power of Four,假設 int 為 32 位元寬度。實作程式碼如下:
題目要求的是 4 的次方數 .
觀察 (num & (num-1))==0
, 帶幾個數字進去 trace 後發現這個條件式是在篩選 num 是不是只有 1 個 bit ?
觀察 4 的次方數 :
4 -> 16 > 64 -> 256 -> 1024
用二進位來表示 :
22 -> 24 -> 26 -> 28 -> 210
由上述觀察可以發現的特性 :
__builtin_ctz
return 的值都是偶數(2,4,6,8…)根據上面的特性寫的特性 , 對應的條件式如下 :
(num & (num-1))==0
!(__builtin_ctz(num) OPQ)
由此可知 OPQ 就是偵測 ctz return 的結果是否為偶數(搭配前面的 !) ,
OPQ 要填 ( f ) 0&1
3
題目 :
針對 LeetCode 1342. Number of Steps to Reduce a Number to Zero,我們可運用 gcc 內建函式實作,假設 int 寬度為 32 位元,程式碼列表如下:
請補完程式碼
要填補上述的程式其實很簡單 , 帶幾次數字後就可以得知答案為
AAA=(a) BBB=(b) , 不過運作原理需要再想想
題目的步驟 :
除 2 相當於往右 shift 1 個 bit ; 減 1 相當於把 LSB 設為 0.
我最初的想法 :
每個 1 都會被設為 0 並且位移 (除了最後一個 bit)
所以把 1 的個數 * 2 減 1 再加上這些 1 中間有幾個 0 再加上右邊有幾個 0 就是答案
照我上面的想法 , 這樣會需要 clz , ctz , popcount 來做運算 , 而且還不知道怎麼算中間的 0 , 跟老師的作法比起來既多一個 function 要呼叫 , 又要算出這些 1 之間的 0 …
後來我換個角度來分析 , 把 num 分 3 區 :
第一區 : clz
第二區 : popcount * 2 - 1 + 中間的 0 個數
第三區 : ctz
答案 : 第二區 + 第三區
最後發現第二、三區的 0 可以算在一起 , 這些數字再加上 popcount 等價於 32 - clz
這時候第三區都算進去了 , 第二區剩下 popcount - 1 , 把這項加進去 : 32 - clz + popcount -1 = popcount + 31 - clz
上述的式子就是此題的答案了!
4
填補下面的程式碼 :
先把兩數共同的 2 次方因數提出來 , 做完輾轉相除法後 , 把公因數乘上該 2 次方因數(藉由 shift 的方式)
程式分析 :
先看 for 迴圈裡的判斷式 : !((u | v) & 1)
&1 代表只跟最右邊的 bit 做 & 運算 , 再觀察回圈內的行為(u 和 v 都除 2) , 所以推斷得出此行程式碼在看是否 u 和 v 均為偶數 .
如果 u 或 v 其中一個為奇數就會跳出迴圈.
此時的狀態 : 2 已經不再是當下的 u 和 v 的公因數了(因為其中一者是奇數)
所以這時先把 u 用成奇數再進 while 迴圈 .
在迴圈裡的行為就是把 v 用成奇數 , 然後作輾轉相除法後就結束了 .
XXX : 由於 do while 迴圈內還有一個迴圈就是看 v 的最後一個 bit 決定它是不是偶數 , 但是如果 v 當下為 0 的話 , 這個迴圈就跑不停了 , 所以 XXX 一定要做到偵測 v 為 0 的時候就停止運作 , 故這題選擇 ( b ) v
YYY : 根據前面的運作原理 , 求完公因數後還要再 shift 原本的 2 次方公因數 , 所以選 ( e ) u << shift
5
題目 :
在影像處理中,bit array (也稱 bitset) 廣泛使用,考慮以下程式碼:
可用 clz 改寫為效率更高且等價的程式碼:
請補完程式碼
其實這題的解答在第三題的題目講解就劇透了( 不知道是巧合還是? )
總是要讓學員偶爾占到好處,學員才會想繼續看下去,人性操作
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
比對修改前和修改後的程式後我發現只有以下的程式碼不太一樣 :
這個迴圈針對 bitset
的 LSB 做判斷後再決定要不要寫入 out[] 裡面 ,
改版後的程式碼 :
先看第二行得知 r 可以知道 bitset 的右邊還有幾個連續 0 , 再跳到第四行看到 bitset 被 t 改變了後進行下一輪 , 這時一樣是看當下的 bitset 的右邊有幾個 0
看到這裡可以推測 , t 可以改變 bitset 的樣子 , 甚至讓 ctz return 的值都不一樣 , 再看一下迴圈的條件 , 就可以推得 :
bitset 最右邊 1 的每次都會被設成 0 !
然後我又想起第三題的某段補充 :
類似的操作還有
x & -x
,將x
的 LSB 取出 (isolate LSB)
由這段得知這個方法
所以這題選 ( e ) bitset & -bitset