数学 —— 其他 —— 快速求逆平方根
【概述】
越底層的函數,調用越頻繁,那么最底層的數學運算函數的優化至關重要。
當求逆平方根時,一般做法都是用函數返回 1/sqrt(x),但在 雷神之錘3 中,有一快速求逆平方根的算法。
【知識儲備】
1.單精度浮點數的存儲
在計算機中,單精度浮點數使用 32 位來存儲,其中,最高位為符號位,后面 8 位為整數位?,代表浮點數的指數,再后面 23 位代表小數部分?,依次表示?、、...
因此,如果 x 是一個正浮點數,則有:
如果想將一浮點數轉為整數形式,則需要做如下運算:
其中,L 是指數部分唯一需要的次數,M 是小數部分對應的整數版本,B 為 127
2.牛頓迭代法
牛頓迭代法,是求解任意連續函數的根值的一種方法。
對于如上函數,現要求這個函數的根:首先猜一個 ,假設它是函數的解,但由于其實際不是,因此需要將這個解迭代,使其逼近真正的解。
在??處作其切線,求得切線方程:,并求切線的根,可以發現對于真正的解已經逼近了一步。
推廣到 n,繼續迭代,就足夠逼近真正的解:
此時, 可被一個統一的函數來表示:
令?ε 為當前解與真正解 r 的距離,則:
綜合方程,可得:
因此,只要?ε 小于某個特定的值,則可認為此時的??與方程的解十分接近
【源碼】
float Q_rsqrt( float number ) {long i;float x2, y;const float threehalfs = 1.5F;x2 = number * 0.5F;y = number;i = * ( long * ) &y;// evil floating point bit level hackingi = 0x5f3759df - ( i >> 1 );//what the fuck?y = * ( float * ) &i;y = y * ( threehalfs - ( x2 * y * y ) );//1st iteration (第一次牛頓迭代)//y = y * ( threehalfs - ( x2 * y * y ) );//2nd iteration, this can be removed(第二次迭代,可以刪除)return y; }【分析】
如果要求一個浮點數的平方根倒數,一般求法為:
將其轉化為關于 y 的方程,有:
轉換為牛頓迭代法的方程,有:
此時,對原方程兩邊同取 2 的對數,有:
由于?,則在這個區間內,可以近似取:?
根據方差的計算,當 σ=0.0430357 時,整體的偏差是最小的,此時上面的等號兩邊應該相當。
將上述公式整合,最終的??可以寫成:
則:
其中:
最終,寫成代碼就是:
i = 0x5f3759df - ( i >> 1 );【關于源碼】
求逆平方根的算法來自 雷神之錘3(quake3) 的作者卡馬克,該算法并不復雜,其核心就是用牛頓迭代法來不斷逼近,但卡馬克真正厲害的地方,在于他選擇了一個十分神秘的常數:0x5f3759df 來計算猜測值,于是第一次牛頓迭代算出的值已經非常接近??,這樣僅需兩次牛頓迭代就可達到所需精度。
普渡大學的數學家 Chris Lomont 看了以后覺得有趣,決定要研究卡馬克弄出來的這個猜測值有什么奧秘。
Lomont 在精心研究之后從理論上也推導出一個最佳猜測值:0x5f37642f,和卡馬克的數字非常接近。
傳奇并沒有在這里結束。
Lomont 計算出結果以后非常滿意,于是拿自己計算出的起始值和卡馬克的神秘數字做比較,看哪個數字能夠更快更精確的求得逆平方根,結果是卡馬克贏了... 誰也不知道卡馬克是怎么找到這個數字的。
最后 Lomont 采用暴力方法一個數字一個數字試過來,終于找到一個比卡馬克數字要好上那么一丁點的數字,雖然實際上這兩個數字所產生的結果非常近似,這個暴力得出的數字是:0x5f375a86。
Lomont 為此寫下一篇論文,"Fast Inverse Square Root"。
論文下載地址:
http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf
http://www.matrix67.com/data/InvSqrt.pdf
總結
以上是生活随笔為你收集整理的数学 —— 其他 —— 快速求逆平方根的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 暑期训练日志----2018.8.16
- 下一篇: 计算字符串距离(信息学奥赛一本通-T12