數學 —— 其他 —— 快速求逆平方根
【概述】
越底層的函式,呼叫越頻繁,那麼最底層的數學運算函式的優化至關重要。
當求逆平方根時,一般做法都是用函式返回 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 hacking
i = 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
相關文章
- Python求一個數的平方根Python
- Lisp求平方根Lisp
- 關於求平方根
- 求平方根 && 牛頓迭代法
- 矩陣求逆矩陣
- scheme 求平方根函式 sqrt 牛頓法實現Scheme函式
- 編寫一個函式求平方根,如果輸入的是負數,丟擲自定義型別的異常。如果輸出的是正數和零,則正常輸出其平方根...函式型別
- 快速求完全二叉樹的節點個數二叉樹
- 一道求餘數小學數學題的解法
- vue3 快速入門系列 —— 其他APIVueAPI
- <<快速入手Rust>>14.HashMap和其他集合RustHashMap
- Quick Pow: 如何快速求冪UI
- LeetCode 69[x的平方根]LeetCode
- 69.x的平方根
- 【數學】快速傅立葉變換(FFT)FFT
- 求完全數個數
- 求眾數
- JavaScript保留兩位或者其他位數小數JavaScript
- 『忘了再學』Shell基礎 — 23、其他環境變數配置檔案變數
- LeetCode LCR072[x的平方根]LeetCode
- 求正整數
- 【leetcode】求眾數LeetCode
- 尤拉計劃705:除數序列的逆轉次數
- 有趣的請求引數/請求頭
- 轉發精品:求極限、求積分、求微分、求導數、求曲,求全微分、求複合求導
- 高等數學隨記 - 利用雙元法求不定積分
- React 快速上手 – 09 資料請求 fetchReact
- React 快速上手 - 09 資料請求 fetchReact
- 機器學習(8)——其他聚類機器學習聚類
- 求插值係數
- 求最大質因數
- 求最小k個數
- 雜湊求眾數
- 矩陣類及其常規運算(加、減、乘、轉置、求逆、行列式、代數餘子式、伴隨矩陣)矩陣
- 前端學習-vue影片學習015-其他API前端VueAPI
- nGrinder中快速編寫groovy指令碼03-在GET請求中傳送引數指令碼
- numpy.linalg包函式用法集錦(求逆矩陣,求矩陣行列式的值,求特徵值和特徵向量,解方程組)函式矩陣特徵
- 爬蟲快速入門——Get請求的使用爬蟲