推薦開源項目:簡單的SLAM與機器人教程與編程實踐-githubhtml
今日瘋言瘋語:
不少算法看不懂大機率是這些算法作出的一些假設你不知道——@Ai醬python
問題:已知A和y,須要求x。而且須要最小化||Ax-y||。
下面是求解x的方法:
Ax=yATAx=ATyx=(ATA)−1ATyAx=y \\ A^TAx = A^Ty\\ x = (A^TA)^{-1}A^TyAx=yATAx=ATyx=(ATA)−1ATy
之因此要乘個ATA^TAT是由於A極可能不是方陣,不是方陣是不能求逆的。ios
問題:已知f()表達式和y,須要求x。而且須要最小化∣∣f(x)−y∣∣||f(x)-y||∣∣f(x)−y∣∣。這個||xx||是指範數的意思,你把它當作是絕對值或者平方就行.而咱們知道∣∣f(x)−y∣∣||f(x)-y||∣∣f(x)−y∣∣的最小值是0,因此咱們就是想求∣∣f(x)−y∣∣||f(x)-y||∣∣f(x)−y∣∣等於0時x的取值。
什麼是牛頓法?
它是一個用來求某個函數最小值所在點的自變量的值算法。牛頓法這和這裏咱們須要求解的問題有什麼聯繫。咱們就是想求g(x)=∣f(x)−y∣g(x)=|f(x)-y|g(x)=∣f(x)−y∣這個函數的最小值點的自變量x。若是你還不瞭解爲什麼要哦用牛頓法,你能夠再看看前面的問題描述。c++
注意:用牛頓法(Newton’s Method)求解一個函數最小值點對應的自變量值時,默認對這個函數有個假設「這個函數的最小值接近於0」。也就是說咱們知道這個函數的最小值。可是就是不知道取得最小值時自變量的值。咱們要用牛頓法求這個自變量的值。git
只要提到牛頓法必定要注意前面這個假設,不少算法看不懂大機率是這些算法作出的一些假設你不知道。github
咱們整理下思路,如今想求g(x)g(x)g(x)的最小值點時候對應的x。而且知道g(x)g(x)g(x)的最小值是0. 牛頓法的思路是隨便先猜最小值點對應的x。而後慢慢修正它直到接近最小值點對應的自變量值。假如我猜是x0x_0x0。而後怎麼修正這個猜想值呢?如今咱們已知一個點(x0,g(x0))(x_0,g(x_0))(x0,g(x0)),而且知道在這個點時函數g(x)的導數值g′(x0)g'(x_0)g′(x0)。那麼咱們根據高中學的點斜式就能夠寫出通過(x0,g(x0))(x_0,g(x_0))(x0,g(x0))這個點的切線方程y−g(x0)=g′(x0)(x−x0)y-g(x_0)=g'(x_0)(x-x_0)y−g(x0)=g′(x0)(x−x0)。咱們能夠把這個切線當作g(x)g(x)g(x)的近似。前面提到了牛頓法有一個假設(函數g(x)的最小值等於0),如今這個假設開始發揮大做用了。如今y−g(x0)=g′(x0)(x−x0)y-g(x_0)=g'(x_0)(x-x_0)y−g(x0)=g′(x0)(x−x0)是對g(x)的近似,那麼咱們能夠令函數值y=0求得一個x。把這個x當作最小值點對應自變量值的一個新的猜想。而後重複以上過程直到找到一個變量xnx_nxn使得g(xn)g(x_n)g(xn)很是接近於0.此時的xnx_nxn就是咱們要求的g(x)的最小值時對應的自變量的值。算法
假如你須要求sin(x)=0.233時的x的取值。注意x的單位是弧度。雖然你能夠調用Python代碼裏面的arcsin這個函數,可是爲什麼咱們不試試本身編程實現arcsin呢?引用某知名網友的話「抱着造輪子的心態學東西(逃」。編程
咱們整理下思路如今的最小二乘法問題就是咱們須要求g(x) = |sin(x)-0.233|的最小值所在點的自變量值x。
在使用牛頓法前咱們不要忘了牛頓法是有個假設的。那就是目標函數g(x)的最小值是0.顯然如今咱們這個例子是知足的。dom
第一步:隨便猜g(x) 最小值所在點對應的自變量值. 如今咱們假設這個自變量值是x0=3.14x0=3.14x0=3.14。因此咱們獲得了一個點(3.14,sin(3.14)−0.233)(3.14, sin(3.14)-0.233)(3.14,sin(3.14)−0.233)。並且知道這個點的導數值g′(3.14)=cos(3.14)g'(3.14)=cos(3.14)g′(3.14)=cos(3.14)。而後咱們能夠寫出通過這個點的切線方程y−(sin(3.14)−0.233)=g′(3.14)∗(x−3.14)y-(sin(3.14)-0.233) =g'(3.14)*(x-3.14)y−(sin(3.14)−0.233)=g′(3.14)∗(x−3.14)。因此切線方程爲y−(sin(3.14)−0.233)=cos(3.14)∗(x−3.14)y-(sin(3.14)-0.233) =cos(3.14)*(x-3.14)y−(sin(3.14)−0.233)=cos(3.14)∗(x−3.14)
第二步:將切線看作是原函數g(x)g(x)g(x)的一個近似,而後令切線函數值y等於0來修正第一步的猜想值。根據0−(sin(3.14)−0.233)=cos(3.14)∗(x−3.14)0-(sin(3.14)-0.233) =cos(3.14)*(x-3.14)0−(sin(3.14)−0.233)=cos(3.14)∗(x−3.14)能夠解得x=−(sin(3.14)−0.233)/cos(3.14)+3.14x=-(sin(3.14)-0.233)/cos(3.14)+3.14x=−(sin(3.14)−0.233)/cos(3.14)+3.14。咱們能夠將這個字做爲g(x) 最小值所在點對應的自變量值的一個新的猜想值。
重複上面兩步直到g(猜想值)很是接近於0.函數
下面是Python代碼:
'''牛頓法求解非線性最小二乘法 author: @Ai醬 歡迎評論 ''' import math import random def g(x0): ''' 目標函數在x0處的函數值 Args: x0 自變量:角度,單位是弧度 ''' return math.sin(x0) + 0.233 def dg_dx(x0): ''' 目標函數g(x)在x0處導數g'(x0) Args: x0 自變量:角度,單位是弧度 ''' return math.cos(x0) # 1. 爲g(x) 最小值所在點對應的自變量值隨便猜一個值 x0 = random.random() while abs(g(x0)) > 0.001: # 一直迭代直到g(x0)接近0 # 2. 令切線函數值y=0求得一個x值,將它做爲一個新的猜想值修正原先的猜想x0 new_x0 = x0 - g(x0)/dg_dx(x0) x0 = new_x0 print("咱們本身寫的牛頓法求得的arcsin(0.233)=",x0) # 咱們用python自帶的arcsin檢驗下 print("Python自帶的arcsin求得的arcsin(0.233)=",math.asin(0.233))
若是你看不懂Python代碼,下面是我寫的c++代碼:
#include<math.h> #include<iostream> using namespace std; /** * 返回g(x)在x0處的函數值g(x0) * params: * x 自變量:角度,單位弧度 */ float g(float x0) { return sin(x0) - 0.2333; } /** * 目標函數g(x)在x0處導數g'(x0) * params: * x0 自變量:角度,單位是弧度 */ float dg_dx(float x0) { return cos(x0); } int main() { // 1. 爲g(x) 最小值所在點對應的自變量值隨便猜一個值 float x0 = 1.233;//這個你隨便設(因爲是弧度因此不要太大) while (abs(g(x0)) > 0.001) // 一直迭代直到g(x0)接近0 { // 2. 令切線函數值y = 0求得一個x值,將它做爲一個新的猜想值修正原先的猜想x0 float new_x0 = x0 - g(x0) / dg_dx(x0); x0 = new_x0; } cout << "咱們本身寫的牛頓法求得的arcsin(0.233)=" << x0 << endl; // 咱們用c++自帶的arcsin檢驗下 cout << "c++自帶的arcsin求得的arcsin(0.233)=" << asin(0.233) << endl; return 0; }