吹水牛頓迭代法

因?yàn)榇邓哪芰Σ患雅敉砸却騻€草稿冷蚂,今天的吹水過程大概是:
1、牛頓迭代法的演繹過程
2痘番、牛頓迭代法求n次方根
3捉片、牛頓迭代法求n次方根改進(jìn)版
4、牛逼哄哄的invsqrt求平方根倒數(shù)

1汞舱、牛頓迭代法的演繹過程

乍一聽伍纫,好像很高大上,其實(shí)理解上沒什么難度兵拢。
牛頓迭代法就是在不斷迭代的過程中逼近曲線的根翻斟,可以看做,它就是用來求曲線的根的说铃。
所以它是怎么個迭代的過程访惜,先上個動圖:

牛頓迭代法的過程

我用吹水的姿勢描述一遍動圖的過程,先是有一個曲線腻扇,然后要找到曲線和x軸的交點(diǎn)债热,這個時候,腦袋一拍幼苛,選x1作為第一點(diǎn)窒篱,在這個點(diǎn)上垂直于x軸的線與曲線的交點(diǎn),在這個交點(diǎn)做與曲線的切線,而切線與x軸的交點(diǎn)就是下一個迭代的點(diǎn)墙杯,不斷重復(fù)這個過程配并,直到找到曲線和x軸交點(diǎn)這個目標(biāo)值。值得注意的是高镐,這個方法是不斷逼近溉旋,所以得到的值可能會與目標(biāo)值存在誤差,而誤差小于一定范圍就可以認(rèn)為是目標(biāo)值了嫉髓。一個更加圖文并茂更加通俗易懂的解釋可以點(diǎn)擊 這個這個這個這個观腊。

2、牛頓迭代法求n次方根

那為什么說牛頓迭代法能用來求n次根呢算行,其實(shí)在1中的目標(biāo)值梧油,即曲線與x軸相交點(diǎn)的x值,假設(shè)你的方程為 f(x) = x2 - 64州邢,當(dāng)f(x) = 0時儡陨,x就是64的2次方根,以此類推量淌,把2換成你想要求的n次方迄委,就可以求出64的n次方根了。

明白了演繹過程类少,那我們就來探索它的代數(shù)實(shí)現(xiàn)。在上代碼前渔扎,先上個圖


牛頓迭代法某一點(diǎn)的求值圖解

每次迭代要求的點(diǎn)就是右綠色箭頭指向的這個點(diǎn)硫狞,在圖中可以看到黑色箭頭標(biāo)識的線段長度為-f(x),f'(x)是對f(x)的求導(dǎo)結(jié)果晃痴,也就是切線的斜率残吩,所以綠色箭頭線段的長度為-f(x)/f'(x),而左綠色箭頭的點(diǎn)為x倘核,所有右綠色箭頭指向的值為x加上綠色線段的長度泣侮,即x-(f(x)/f'(x))。

上面的代數(shù)實(shí)現(xiàn)完成后紧唱,就可以上代碼了活尊。嘻嘻嘻,最近在看Python版的SICP漏益,所以貼上Python的代碼蛹锰,這篇文章也是因?yàn)榭戳诉@本書的1.6章節(jié)寫的。def就是定義一個方法的意思绰疤。

def newton_nth_root_of_a(n, a, x = 1, e = 1e-13):
    def f(x):
        return x ** n - a
    def df(x):
        return n * x ** (n - 1)
    
    gap = abs(f(x))
    while gap > e:
        x = x - f(x)/df(x)
        gap = abs(f(x))
    return x
    
print(newton_nth_root_of_a(1, 64))  #64.0
print(newton_nth_root_of_a(2, 64))  #8.0
print(newton_nth_root_of_a(3, 64))  #4.0
print(newton_nth_root_of_a(4, 64))  #2.82842712474619
print(newton_nth_root_of_a(5, 64))  #2.29739670999407
print(newton_nth_root_of_a(6, 64))  #2.0

這里是在線可運(yùn)行版本 铜犬,不多不少,剛好十行,newton_nth_root_of_a方法定義中癣猾,n代表冪敛劝,a代表對a求根,x代表起始值纷宇,e代表誤差范圍夸盟,x和e已經(jīng)設(shè)置了缺省值。f(x)是曲線呐粘,df(x)是f(x)的求導(dǎo)結(jié)果满俗,在while循環(huán)中,x - f(x)/df(x)就是我們上面代數(shù)實(shí)現(xiàn)中的結(jié)果作岖,直到f(x)的值小于誤差唆垃,x就是最后的結(jié)果。

3痘儡、牛頓迭代法求n次方根改進(jìn)版

明白了1辕万、2其實(shí)整個過程就是這樣了,那為啥有3沉删,因?yàn)橐仓^皮接著吹水??渐尿。
2中的10行代碼很簡潔,但是假如我要不斷的求某些值的2次根矾瑰,就要不斷調(diào)用newton_nth_root_of_a(2, a)砖茸。那么我們來改進(jìn)下代碼,保持上面的代碼不變殴穴,增加下面的代碼:

def curry_nth_root(n):
    def curry_nth_root_of_a(a):
        return newton_nth_root_of_a(n, a)    
    return curry_nth_root_of_a

_2th_root = curry_nth_root(2)

print(_2th_root(64)) #8.0
print(_2th_root(49)) #7.000000000000001
print(_2th_root(36)) #6.0
print(_2th_root(25)) #5.0
print(_2th_root(16)) #4.0
print(_2th_root(9))  #3.0

這里是在線可運(yùn)行版本 凉夯,哇咔咔,在curry化之后采幌,就可以方便的求n次方根了劲够,不用每次都輸入n。

curry化其實(shí)也很簡單休傍,那么我們就來個極端點(diǎn)的抽象代碼:

def improve_guess(update, close, guess=1):
    '''返回猜想值征绎,update為更新方法,close為逼近方法磨取,guess為初始猜想值'''
    while not close(guess):
        guess = update(guess)
    return guess
    
def newton_update(f, df):
    '''返回牛頓迭代的計(jì)算函數(shù), f為原函數(shù)人柿,df是對其求導(dǎo)'''
    def update(x):
        return x - f(x) / df(x)
    return update
    
def newton_find_zero_of_f(f, df, tolerance=1e-13):
    '''找出f(x)=0時,x的值忙厌,tolerance為與目標(biāo)真實(shí)值的誤差'''
    def near_zero(x):
        return is_eq(f(x), 0, tolerance)
    return improve_guess(newton_update(f, df), near_zero)
    
def is_eq(x, y, tolerance):
    '''x與y的差值是否在誤差范圍內(nèi)'''
    return abs(x - y) < tolerance
    
def nth_root_of_a(n, a):
    def f(x):
        return x ** n - a
    def df(x):
        return n * x ** (n - 1)
    return newton_find_zero_of_f(f, df)
    
print(nth_root_of_a(1, 64))  #64.0
print(nth_root_of_a(2, 64))  #8.0
print(nth_root_of_a(3, 64))  #4.0
print(nth_root_of_a(4, 64))  #2.82842712474619
print(nth_root_of_a(5, 64))  #2.29739670999407
print(nth_root_of_a(6, 64))  #2.0

這里是在線可運(yùn)行版本 顷扩,把每一步都抽象出來,哈哈哈哈哈慰毅,這看上去很帥隘截,雖然實(shí)際上沒啥必要,最后也可以再curry化。這是個極端的例子婶芭,但是根據(jù)業(yè)務(wù)的需要东臀,我們可以把需要復(fù)用的步驟抽象出來,而抽象的程度也要根據(jù)業(yè)務(wù)開展犀农。

4惰赋、牛逼哄哄的invsqrt求平方根倒數(shù)

吹水時間又到了,究竟這個牛頓迭代法有個毛線用呵哨。那你就要google下invsqrt這個改變游戲史進(jìn)程的函數(shù)赁濒,可以看下知乎上的這個回答 有哪些算法驚艷到了你?孟害。這個函數(shù)到底是干嘛的拒炎,就是求某個數(shù)的2次方根的倒數(shù)。因?yàn)檫@個函數(shù)在3d引擎的中經(jīng)常使用挨务,所以提高這個函數(shù)的效率是至關(guān)重要的击你,這個函數(shù)比系統(tǒng)的1/sqrt(x)快了一半。雖然是倒數(shù)谎柄,但是也是能用牛頓迭代法的啊丁侄。為毛啊,因?yàn)檫@也是曲線啊朝巫,過程跟1中的圖相識鸿摇。

但是!E场;琛!請看代碼

float InvSqrt (float x){
    float xhalf = 0.5f*x;
    int i = *(int*)&x;
    i = 0x5f3759df - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    return x;
}

第六行就是利用牛頓迭代法的值x - f(x)/f'(x)演算后的結(jié)果糙臼,在這里f(x) = 1/x2,則f'(x)=2/x3恩商。
第三行是把一個float指針強(qiáng)轉(zhuǎn)成int指針变逃,從而進(jìn)行右移運(yùn)算,因?yàn)閒loat類型不能進(jìn)行位運(yùn)算怠堪,從而得到x值指數(shù)部分/2的結(jié)果揽乱,額,這里我不打算展開說啊粟矿,因?yàn)槲艺f不清楚啊啊啊啊凰棉,我引用一段別人的解釋,詳情請移步這里

所以陌粹,32位的浮點(diǎn)數(shù)用十進(jìn)制實(shí)數(shù)表示就是:M2E撒犀。開根然后倒數(shù)就是:M(-1/2)2^(-E/2)。現(xiàn)在就十分清晰了。語句i> >1其工作就是將指數(shù)除以2或舞,實(shí)現(xiàn)2(E/2)的部分荆姆。而前面用一個常數(shù)減去它,目的就是得到M(1/2)同時反轉(zhuǎn)所有指數(shù)的符號映凳。

至于0x5f3759df胆筒,這就相當(dāng)無解了。wiki的介紹中诈豌,這一句的注釋是這樣的:

    i  = 0x5f3759df - ( i >> 1 );               // what the fuck?(這他媽的是怎么回事仆救?)

關(guān)于這個值后來也有人專門去推理過,還寫成論文矫渔,但是彤蔽,我肯定是看不懂的!0稣丁铆惑!
因?yàn)檫@一步,取到一個非常合適的初始值送膳,只需要一次牛頓迭代就能知道目標(biāo)值啦员魏,一次!5K貉帧!

最后

沒有最后碌补,今天吹水結(jié)束B彩!O谜隆U蛟取!M嗫小:骨帧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末群发,一起剝皮案震驚了整個濱河市晰韵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熟妓,老刑警劉巖雪猪,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異起愈,居然都是意外死亡只恨,警方通過查閱死者的電腦和手機(jī)译仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坤次,“玉大人古劲,你說我怎么就攤上這事$趾铮” “怎么了产艾?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滑绒。 經(jīng)常有香客問我闷堡,道長,這世上最難降的妖魔是什么疑故? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任杠览,我火速辦了婚禮,結(jié)果婚禮上纵势,老公的妹妹穿的比我還像新娘踱阿。我一直安慰自己,他們只是感情好钦铁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布软舌。 她就那樣靜靜地躺著,像睡著了一般牛曹。 火紅的嫁衣襯著肌膚如雪佛点。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天黎比,我揣著相機(jī)與錄音超营,去河邊找鬼。 笑死阅虫,一個胖子當(dāng)著我的面吹牛演闭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颓帝,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼米碰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了躲履?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤聊闯,失蹤者是張志新(化名)和其女友劉穎工猜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菱蔬,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡篷帅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年史侣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魏身。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惊橱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箭昵,到底是詐尸還是另有隱情税朴,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布家制,位于F島的核電站正林,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏颤殴。R本人自食惡果不足惜觅廓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涵但。 院中可真熱鬧杈绸,春花似錦、人聲如沸矮瘟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芥永。三九已至篡殷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間埋涧,已是汗流浹背板辽。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棘催,地道東北人劲弦。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像醇坝,于是被迫代替她去往敵國和親邑跪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容