contributed by yyyyuwen
以下為有可能造成 overflow 的程式碼:
注意:確保大小數的順序,不然也有可能會導致輸出結果不同的問題。
當
(high - low) < 0
時,值會是
當(high - low) > 0
時,值會是
其中 EXP1
為 a
, b
, 1
(數字) 進行某種特別 bitwise 操作,限定用 OR, AND, XOR 這三個運算子。
a >> 1
和 b >> 1
的意思是將數值除以2。而 a
和 b
都是 uint32_t
的型態,若是奇數無法整除小數會直接被省去。因此若遇上這種狀況時,做完 (a >> 1) + (b >> 1)
之後還要再加上 1。a & 1
:檢查 a 的 Least Significant Bit 是否為 1 (即檢查是否為奇數),若是則回傳 1a & b & 1
。舉例:
其中 EXP2
和 EXP3
為 a
和 b
進行某種特別 bitwise 操作,限定用 OR, AND, XOR 這三個運算子。
參考 加法運算
a + b
可以變成是 a XOR b + (a AND b) << 1
a XOR b
: a + b 但不進位(a AND b) << 1
: 檢查 a
, b
Most Significant Bit是不是同為 1,如果是則進位。SUM :取得未進位時的SUM
a | b | a ^ b | a + b |
---|---|---|---|
0 | 0 | 0 | |
0 | 1 | 1 | |
1 | 0 | 1 | |
1 | 1 | 0 | (需要進位) |
CARRY :取得是否進位的資訊
a | b | a & b | a + b |
---|---|---|---|
0 | 0 | 0 | -> 不進位 |
0 | 1 | 0 | -> 不進位 |
1 | 0 | 0 | -> 不進位 |
1 | 1 | 1 | -> 進位 |
(a + b) / 2
式子分解:
= ( a + b ) >> 1
= (( a ^ b ) + ( a & b ) << 1) >> 1
= a & b + ( a ^ b ) >> 1
a & b
a ^ b
再做實驗時遇到一個問題:當用 uint32_t
的時候做 (a + b)/2
會 overflow 沒問題,但當如果使用 uint8_t
的時候就不會有 overflow的問題。目前還沒找到原因。
延伸問題:
移動平均(Moving average),又稱滾動平均值、滑動平均,在統計學中是種藉由建立整個資料集合中不同子集的一系列平均數,來分析資料點的計算方法。
移動平均通常與時間序列資料一起使用,以消除短期波動,突出長期趨勢或周期。短期和長期之間的閾值取決於應用,移動平均的參數將相應地設置。例如,它通常用於對財務數據進行技術分析,如股票價格、收益率或交易量。它也用於經濟學中研究國內生產總值、就業或其他宏觀經濟時間序列。
max
):以下是該文章提出的 min 程式碼實作
注意:這實作僅在 INT32_MIN <= (a - b) <= INT32_MAX 成立時,才會發揮作用。
而我們要實作 max
的方法
以下為了方便,將 ((EXP4) & -(EXP5))
設成 x
觀察題目,有兩種狀況:
a ^ 0 = a
-> x = 0
a ^ a ^ b = b
-> x = a ^ b
依上述狀況可以判斷
EXP4
可以填入 a ^ b
EXP5
作為判斷 0 或 1
-(EXP5)
是 0 ( ) -> a > b
-(EXP5)
是 -(1) ( ) -> a < b
EXP5
填入 a < b
延伸問題:
請在 Linux 核心原始程式碼中,找出更多類似的實作手法。請善用 git log 檢索。
原本程式
改寫成
註: GCD 演算法
(u | v) & 1
判斷是否同為偶數,shift
紀錄總共做了幾次 right shift 的運算 ( <<1 )。
4. If x is even and y is odd, .
5. On similar lines, if x is odd and y is even, then . It is because is not a common divisor.
確保 u
為奇數:
輾轉相除法:
利用 while
去遞迴的找餘數,使用連續減法來找出相除的餘數。
而根據輾轉相除法的定理,中止條件則是除到餘數為0。
在上述程式中
u = v
是商數的部份,而 v = t
是餘數的部份,所以 COND
指的就是餘數的部份也就是當 v
等於0的時候變中止迴圈; 因此 COND = v
。
而 u
則是最後的最大公因數,但記得要乘回一開始 shift 的部份,真正的最大公司數應為 。
因此 RET = u << shift
。
延伸問題:
__builtin_ctz
改寫 GCD,分析對效能的提升;考慮 GNU extenstion 的 __builtin_ctzll的行為是回傳由低位往高位遇上連續多少個 0 才碰到 1。
範例: 當 a = 16
16 這個十進位數值的二進位表示法為 00000000 00000000 00000000 00010000
從低位元 (即右側) 往高位元,我們可發現 0 → 0 → 0 → 0 → 1 ,於是 ctz 就為 4,表示最低位元往高位元有 4 個 0
用以改寫的程式碼如下:
其中第 9 行的作用是找出目前最低位元的 1
,並紀錄到 t
變數。舉例來說,若目前 bitset
為 000101b,那 t
就會變成 000001b,接著就可以透過 XOR 把該位的 1
清掉,其他保留 (此為 XOR 的特性)。
若 bitmap 越鬆散 (即 1
越少),於是 improved
的效益就更高。
請補完程式碼。書寫規範:
可以從題目得知該行的目的是為了找出最低位元的1,可以透過二補數的原理來達成目的。即 bitset & -bitset
。
舉例
關於其他程式碼的解釋:
k
代表的是64位 bitset 在整個 bitmap 中的編號
r
則是 bitmap 中末端為零的數量
延伸問題: