quake-iii arena (雷神之鎚3)是90年代的經典遊戲之一。
該系列的遊戲不但畫面和內容不錯,而且即使計算機配置低,也能極其流暢地執行。這要歸功於它3d引擎的開發者約翰-卡馬克(john carmack)。事實上早在90年代初dos時代,只要能在pc上搞個小動畫都能讓人驚嘆一番的時候,john carmack就推出了石破天驚的castle wolfstein, 然後再接再勵,doom, doomii, quake...每次都把3-d技術推到極致。他的3d引擎**資極度高效,幾乎是在壓榨pc機的每條運算指令。當初ms的direct3d也得聽取他的意見,修改了不少api。
最近,quake的開發商id software遵守gpl協議,公開了quake-iii的原**,讓世人有幸目睹carmack傳奇的3d引擎的原碼。
我們知道,越底層的函式,呼叫越頻繁。3d引擎歸根到底還是數**算。
那麼找到最底層的數**算函式(game/code/q_math.c),必然是精心編寫的。裡面有很多有趣的函式,很多都令人驚奇,估計我們幾年時間都學不完。
在/code/game/q_math.c裡發現了這樣一段**。
它的作用是將乙個數開平方並取倒,經測試這段**比(float)(1.0/sqrt(x))快4倍:
float q_rsqrt( float number )
函式返回1/sqrt(x),這個函式在影象處理中比sqrt(x)更有用。
注意到這個函式只用了一次疊代!(其實就是根本沒用疊代,直接運算)。編譯,實驗,這個函式不僅工作的很好,而且比標準的sqrt()函式快4倍!要知道,編譯器自帶的函式,可是經過嚴格子細的彙編優化的啊!
這個簡潔的函式,最核心,也是最讓人費解的,就是標註了「what the ****?」的一句:i = 0x5f3759df - ( i >> 1 );
再加上y = y * ( threehalfs - ( x2 * y * y ) );
兩句話就完成了開方運算!而且注意到,核心那句是定點移位運算,速度極快!特別在很多沒有乘法指令的risc結構cpu上,這樣做是極其高效的。
演算法的原理其實不複雜,就是牛頓迭代法,用x-f(x)/f'(x)來不斷的逼近f(x)=a的根。
簡單來說比如求平方根,f(x)=x^2=a ,f'(x)= 2*x,f(x)/f'(x)=x/2,把f(x)代入x-f(x)/f'(x)後有(x+a/x)/2,現在我們選a=5,選乙個猜測值比如2,那麼我們可以這麼算
5/2 = 2.5;
(2.5+2)/2 = 2.25;
5/2.25 = ***;
(2.25+***)/2 = ***x
這樣反覆迭代下去,結果必定收斂於sqrt(5),沒錯,一般的求平方根都是這麼算的,但是卡馬克(quake3作者)真正牛b的地方是他選擇了乙個神秘的常數0x5f3759df 來計算那個猜測值,就是我們加注釋的那一行,那一行算出的值非常接近1/sqrt(n),這樣我們只需要2次牛頓迭代就可以達到我們所需要的精度.好吧 如果這個還不算nb,接著看:
普渡大學的數學家chris lomont看了以後覺得有趣,決定要研究一下卡馬克弄出來的這個猜測值有什麼奧秘。lomont也是個牛人,在精心研究之後從理論上也推導出乙個最佳猜測值,和卡馬克的數字非常接近, 0x5f37642f。卡馬克真牛,他是外星人嗎?
傳奇並沒有在這裡結束。lomont計算出結果以後非常滿意,於是拿自己計算出的起始值和卡馬克的神秘數字做比賽,看看誰的數字能夠更快更精確的求得平方根。結果是卡馬克贏了... 誰也不知道卡馬克是怎麼找到這個數字的。
最後lomont怒了,採用暴力方法乙個數字乙個數字試過來,終於找到乙個比卡馬克數字要好上那麼一丁點的數字,雖然實際上這兩個數字所產生的結果非常近似,這個暴力得出的數字是0x5f375a86。
最後,給出最精簡的1/sqrt()函式:
float invsqrt(float x)
記錄幾個開平方演算法
整數開平方演算法 本演算法只採用移位 加減法 判斷和迴圈實現,因為它不需要浮點運算,也不需要乘除運算,因此可以很方便地運用到各種晶元上去。我們先來看看10進製下是如何手工計算開方的。先看下面兩個算式,x 10 p q 1 公式 1 左右平方之後得 x 2 100 p 2 20pq q 2 2 現在假...
記錄幾個開平方演算法
整數開平方演算法 本演算法只採用移位 加減法 判斷和迴圈實現,因為它不需要浮點運算,也不需要乘除運算,因此可以很方便地運用到各種晶元上去。我們先來看看10進製下是如何手工計算開方的。先看下面兩個算式,x 10 p q 1 公式 1 左右平方之後得 x 2 100 p 2 20pq q 2 2 現在假...
基於牛頓法的開平方實現
牛頓法是求解最優問題的一種常用方法。本文主要在實現使用牛頓法進行開平方操作,使用python實現。在開平方求解中,更多的可以參考這篇文章。牛頓法是一種最優化求解方法,在迭代過程中求取到最接近解的值,類似的還有梯度下降等等。這裡主要講解我對該方法的推導,有錯誤之處麻煩指出。首先針對開根號求解,可用以下...