contributed by yu-hsiennn
假定 為欲所求之平方根
經展開後,化簡可得
令 為 ,且此輪與上一輪差設為 ,即
我們再將式子利用 及 化簡
再觀察下一輪的變化
因此
改用第二周測驗 2 的 __ffs
在一開始看到這個時,完全無法理解是怎麼跑出這些等號的。
後來才發現,其實有段開,只是 HackMD
沒有顯示出來
因為乘法及除法的運算成本很高,故想利用位移及加減的方式去達到相同的效果
此處數字範圍為 0~19
,只要這一段的計算有達到預期即可
題目是用 N = 128
來逼近 1/10
也就是說可以
但這邊會忽略掉一些值,如果要精確的話可以把他加回去
不過在範圍 0~19
其實並不會影響結果
如果範圍是 0~19
,這邊做了一些測試
結果為
而題目
可以看到這邊的作法是
所以將 q 右移 3 可得
而最後是在計算 *mod = in - *div * 10
利用 4 層迴圈來找尋以 2 為底的對數
65536
的數值,直到小於才離開此迴圈256
的數值,直到小於才離開此迴圈16
的數值,直到小於才離開此迴圈2
的數值,直到小於才離開此迴圈,並回傳結果舉個例子:
n = 2049
2049 < 65536, (不執行第一個迴圈)
2049 >= 256, n: 8, result: 8
8 < 16, (不執行第三個迴圈)
8 >= 2, n: 4, result: 9
4 >= 2, n: 2, result: 10
2 >= 2, n: 1, result: 11
1 < 2, 回傳 result: 11
我們也可以換個想法,利用找尋目前 n
第一個 1
的前方有幾個 0
(__builtin_clz(n)),再用 31 去減掉前方 0
的個數即可
參考 log2.h,了解到 linux 核心內部 ilog2
的實作方式,其中有以下幾點:
n
是否使用了常數時間來編譯fls
fls
之所以需要減 1 是因為它是以 1
為底根據公式
化簡後可得
而 avg->factor
的用途在於,不是每個系統都支援浮點運算,因此藉由固定其大小來確保精確度
開頭的 x--
用意在於,若是 x
為 2 的冪,最後的 +1
會造成結果錯誤,所以先將 x--
來避免這個情況發生。
而利用後面一連串的判斷,來快速找出 x
的對數。
最後的回傳會判斷 x
是否還有值,且合併 r
與 shift
的值,並加上最後的進位 1
。
x = 0
的狀況可以發現到,當我們直接帶入 0
及 1
時,程式會回傳結果 1
,而這並不是我們所預期的,於是可以如下修改
想法是:
0
,為 0
則不做 -1x
是否為 0
,是則不做 +1如此,即可以避免當 x
為 0
和1
,回傳的錯誤結果 1
,並維持 branchless
計算方式採取每 4 個位元為單位,
右移 1 且只把最高位元(以 4 位元為單位)給變成0,以防其影響計算結果,
跑完這段即可得,
接下來會執行 v = (v + (v >> 4)) & 0x0F0F0F0F
令 , ()
所以
最後將 v *= 0x01010101, v >> 24
,即可得
時間複雜度為
令 ,
可以改寫成
我們可以利用 n = popcount(n & 0x55555555) - popcount(n & 0xAAAAAAAA)
,來達到相同效果
其中還可以利用以下定理來進一步的化簡
即 popcount(m) = 16, (m = 0xAAAAAAAA)
,而為了避免 n = popcount(n ^ 0xAAAAAAAA) - 16
結果為負數,我們可以加入一個足夠大的 3 的倍數的數字給 -16
,範例為 39 - 16 -> 23
再來執行 n = popcount(n ^ 0x2A) - 3
來限縮範圍至 -3 ~ 3
,最後的 return n + ((n >> 31) & 3)
再將負的轉為正的
其中,參考 mesohandsome 的筆記,發現到要將 n
做轉型才會得到正確結果,即
當連線時,會是 0x7
,為了 0x7 + ? == 0x8
要為 true
,所以 ?
應為 0x1
先將 x
右移 15
來保留其商數,再利用 x & UINT32_C(0x7FFF)
來保留最低的 15
個位元
xt_create
: 初始化一棵新的 XTree
xt_destroy
: 利用遞迴的方式來將樹的每個節點的資源給釋放掉xt_frist
: 找到樹中最小值的節點(最左邊)xt_last
: 找到樹中最大值的節點(最右邊)xt_rotate_left
: 執行下圖操作xt_rotate_right
: 執行下圖操作xt_balance
: 回傳左子樹樹高減去右子樹樹高xt_update
: 確認樹是否平衡,不平衡則執行對應的旋轉操作xt_insert
: 將新節點插入進樹中xt_remove
: 將對應的節點從樹中移除想法是若欲刪除之節點在右子樹,則用右子樹的最小來填補,即 least
,而填補完需要確認樹是否平衡,所以執行 xt_update(root, xt_right(least))
,
反之,則用左子樹的最大,即 most
,且更新 xt_update(root, xt_left(most))
最後在判斷整棵樹有沒有平衡,即 xt_update(root, parent)