来看看比尔盖茨当年写的BASIC解释器源代码吧,你就知道泰勒级数有什么用了...
幾年前當我剛上大學那會,我曾經問過我一位學計算機同學的一個問題:計算機是如何計算諸如??或者??這種運算的?當初這個問題曾經困擾了我好長時間,這個問題并非是我當年在微積分課堂上解決的,而是直到我后來接觸編程后才徹底恍然大悟。那么計算機究竟是如何計算這類運算呢?帶著這個問題,我們先來看看當年比爾蓋茨在上世紀七十年代寫的BASIC解釋器的部分源代碼吧。
代碼部分內容:
完整源代碼鏈接
https://www.pagetable.com/docs/M6502.MAC.txt
蓋茨的這些代碼公布于兩年前,當時在網上引起了不小風波。
對于這段匯編代碼,即便你看不懂,但你僅從注釋部分(我標注的紅框內)也能猜出是計算正弦sin函數的方法。如果你匯編基礎好的話,仔細研究,發現蓋茨用的正是泰勒公式來逼近這個??函數的。也即:?暫且不說編程邏輯、算法效率方面如何,單是從注釋習慣方面已經足夠秒殺現在大部分的軟件從業人員了。如果非要說算法的話,這個算法已經是被蓋茨壓榨到了極限,開始部分就先判斷象限,再將角度進行區間的轉化。
要知道,在比爾蓋茨那個年代,市場上還沒有軟件工程這個概念。。。而蓋茨這部分代碼卻包含了完整的注釋,極簡的算法。想想多恐怖。
嗯,扯遠了,今天我們不聊匯編,主要聊一聊蓋茨使用的這個算法,也就是泰勒級數。
蓋茨使用的這個算法,延續到了現代計算器上面。
無獨有偶,前不久微軟在GitHub上面開源了其Windows上面自帶計算器應用的C++源代碼(計算引擎部分用C++,UI部分使用C#)
部分C++源代碼:
完整代碼且看
https://github.com/Microsoft/calculator
對于上面的代碼,得益于微軟工程師完美的代碼注釋,即便你沒有編程基礎的話,你依然能夠從注釋里面看出本行代碼的大致意思,沒錯,這段代碼就是求余弦??的函數。再仔細觀察注釋部分,你能發現,哈哈,你能發現微軟的工程師挺皮的,居然把??函數的泰勒展開給畫出來了。我甚至懷疑微軟的工程師是不是繼承了蓋茨的編碼注釋風格,居然如此詳細。
如果那兩段匯編你可能看不懂的話,那么上面這段C++代碼你肯定能有所領悟。其實這段代碼跟上面那段匯編有同樣的功能,都是計算三角函數,而所用的方法也一樣,都是泰勒展開。至于微軟為什么不直接使用c++提供的math庫函數,而非要再造一個輪子來計算這類函數,我個人猜測可能有兩個原因,一個是歷史遺留問題,也許早年c++還沒有math庫,另一個我覺得使用庫函數的話會造成編譯出來的二進制文件過大,也許微軟也擔心這個問題。當然這都是我個人的猜測,但這里不做重點討論。
其實,計算器求解我們所知道的幾乎所有的函數都是使用泰勒展開方法,例如??,??,??等等。
那么泰勒級數為什么能逼近我們我們所需要的函數曲線呢?為了解決這個問題,我們先從一個簡單速度的問題說起。
假設小明坐在一輛車上,車的行駛速度隨時間??變化為??.小明突然很想知道當??時刻他的行駛速度。這可把小明給難住了,因為他手上沒計算器,又沒三角函數表可查。如果是特殊的函數點位的話,大不了他運用運用和差公式直接就可以計算出來,但是這種情況該怎么辦呢?
萬般無奈的小明想到,如果我能把這個速度指數函數轉化成只有加法或者乘法的函數,那該多好。
小明想到,如果定義一個只有加法乘法的函數??,讓它在某點的值與??的值一樣,后面為了保證它們兩個的增長趨勢一樣,讓它們兩個的速度的變化率(導數)一樣,變化率的變化率(導數的導數)也一樣,變化率的變化率的變化率(導數的導數的導數)也一樣。。。,那么后面的兩者函數的值不應該是很接近嗎??在一定條件下成立呢?
于是,小明定義了一個只有加法與乘法的函數??來表示所有可能的多項式:?可是如何才能讓g(x)逼近f(x)呢,換句話說,如何取得合適的??才能讓?成立呢?
小明觀察了??的函數圖像:
他發現,在(-π,π)區間之內,??函數的圖像很像拋物線。舍繁取簡,他也用拋物線來近似。于是他講??簡化,定義為:?
可即便這樣,還是要找出三個變量的值。于是小明進一步思考,已經知道??了,求得的??至少也得讓他們兩者在??處相等,也即:?代入計算:
也即??變成:?可是在那么多的??與??當中,如何取值呢?
于是,小明再觀察圖像,他為了保證在??附近這個拋物線逼近??,他讓兩者的切線斜率一致,這樣就保證了兩個函數的加速度保持一致,也即:?
代入計算:?也即??,這樣??也就更簡單了,直接剩下最后一個系數:?這時候小明再觀察圖像,發現兩者似乎又進了一步:
為了求出最后一個??,小明再觀察圖像:
小明發現在??附近,也即(-π/2,π/2)附近,??是一個凸函數,而且斜率不斷減小,這時為什么不讓他們的斜率的增長率也保持一致呢?也就是說讓兩個函數在??處加速度的加速度也相等,也即他們的二階導數相等:?代入計算:?求得??,從而得到?再觀察圖像:
這時候發現,經過三次的求導運算,兩者函數在??周圍已經非常接近了。我們來實際驗證一下用??逼近??的誤差有多大:??兩者誤差僅為0.00005,完全在我們可接受的范圍之內。于是乎,小明忽然明白了為什么??與??是等價無窮小了(當初的我曾經對這個公式大惑不解)。
可是小明依舊不滿足,他在想我只求了兩次的倒數,如果我讓變化率的變化率的變化率的變化率的變化率。。。。也相等的話,誤差會不會更小呢?
于是小明持續往下計算
發現越往后,越精確,但是代價就是計算量越大。而往往,我們直接舍去后面的高階無窮小,僅需要前三四項即可滿足我們的要求。
可是,小明突然又想到,這樣只是計算在??處附近的函數值,如何讓??逼近??的任意一點比如??呢?很簡單只要將??替換成??即可,推導過程也一樣,只是不會像x=0處那樣把奇數項給消掉了:
小明大喜,用這種方法在沒有計算器的情況下幾乎可以計算任何函數的數值了,雖然有一定誤差,但誤差完全在我們容忍范圍之內。于是他又用同樣的方法計算了??在??處的泰勒展開:
具體步驟為:
1),先讓函數值相同,也即?得到:??2),再讓一階導數相等?
得到:?......
最后讓n階導數數相等 (??的??階導數仍是??)
得到:?最后得出:
當??不斷增大的時候,小明驚奇地發現在固定區間內,兩者幾乎完美地融合在了一起:
可是小明縱然知道了其中的推導原理,但他依然不太直觀理解泰勒級數為什么會逼近原函數。我在上文「深入淺出線性代數的理解及應用」中曾經引用笛卡爾的名言,說明幾何對抽象代數理解的重要性。同樣,在這里也不例外,泰勒級數在幾何上也有明確的意義:
泰勒級數的幾何意義
如圖下所示,我們假設黃色的曲線函數為??,在??區間內連續。點??為點??周圍(微小的鄰域)內任意一點。
我們再定義一個積分函數:?由于??是??的原函數(注意,??是積分函數,??才是曲線函數),因此:?
積分函數??連續,因此可以分成兩個積分區間:?這兩個積分區間實際上由三部分構成。根據積分的實際幾何意義,我們知道??代表圖中陰影部分的面積,我們記為??.而面積A由三部分構成,分別是??掃過的曲邊多邊形?~?,矩形??,曲邊三角形??:
我們記三部分面積分別為?~?,也即:
~
曲線??在任意一點的切線斜率為??,曲邊三角形的斜邊我們可以用??在點??上的切線來近似表示(這便是我在上篇文章深入淺出線性代數的理解及應用中提到的微分的“非線性函數的局部顯性化”的重要思想)。顯然,這里在??處斜率為??,也即??.如果??越接近??,那么這個斜邊越接近曲線??:
切線??在點??處的斜率為??,也即??,因此三角形的直角邊高??為:?θ從而得出三角形的面積為:?也即?而這個矩形的高即??,它的面積:?
也即:?顯然曲邊多邊形也就是第一部分的面積為??:?~三個面積相加,也就是?
上面這個式子不正是泰勒級數展開的二次多項式嗎,不過不要忘了,這個式子與??之間的關系是約等于。如果??無限逼近??,那么我們完全可以用??來近似??,也就是說省略掉后面的矩形與三角形,只剩下一項。而實際上我們省略了后面的高階無窮小,在一般情況下,精度已經夠高了。
這便是泰勒級數在幾何上面的解釋。
推薦閱讀:
? ??專輯|Linux文章匯總
? ??專輯|程序人生
? ??專輯|C語言
嵌入式Linux
微信掃描二維碼,關注我的公眾號?
總結
以上是生活随笔為你收集整理的来看看比尔盖茨当年写的BASIC解释器源代码吧,你就知道泰勒级数有什么用了...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 诺,你们要的Python进阶来咯!【函数
- 下一篇: php常用数组函数