推薦開源項目:簡單的SLAM與機器人教程與編程實踐-github
今日瘋言瘋語:
很多算法看不懂大概率是這些算法做出的一些假設你不知道——@Ai醬
線性最小二乘法||Ax-y||可以直接求解
問題:已知A和y,需要求x。並且需要最小化||Ax-y||。
下面是求解x的方法:
之所以要乘個是因爲A很可能不是方陣,不是方陣是不能求逆的。
非線性最小二乘法需要用牛頓法求解
問題:已知f()表達式和y,需要求x。並且需要最小化。這個||xx||是指範數的意思,你把它當做是絕對值或者平方就行.而我們知道的最小值是0,所以我們就是想求等於0時x的取值。
什麼是牛頓法?
它是一個用來求某個函數最小值所在點的自變量的值算法。牛頓法這和這裏我們需要求解的問題有什麼聯繫。我們就是想求這個函數的最小值點的自變量x。如果你還不瞭解爲何要哦用牛頓法,你可以再看看前面的問題描述。
牛頓法思想是怎樣的?具體步驟怎麼做?
注意:用牛頓法(Newton’s Method)求解一個函數最小值點對應的自變量值時,默認對這個函數有個假設“這個函數的最小值接近於0”。也就是說我們知道這個函數的最小值。但是就是不知道取得最小值時自變量的值。我們要用牛頓法求這個自變量的值。
只要提到牛頓法一定要注意前面這個假設,很多算法看不懂大概率是這些算法做出的一些假設你不知道。
我們整理下思路,現在想求的最小值點時候對應的x。並且知道的最小值是0. 牛頓法的思路是隨便先猜最小值點對應的x。然後慢慢修正它直到接近最小值點對應的自變量值。假如我猜是。然後怎麼修正這個猜測值呢?現在我們已知一個點,並且知道在這個點時函數g(x)的導數值。那麼我們根據高中學的點斜式就可以寫出經過這個點的切線方程。我們可以把這個切線當做的近似。前面提到了牛頓法有一個假設(函數g(x)的最小值等於0),現在這個假設開始發揮大作用了。現在是對g(x)的近似,那麼我們可以令函數值y=0求得一個x。把這個x當做最小值點對應自變量值的一個新的猜測。然後重複以上過程直到找到一個變量使得非常接近於0.此時的就是我們要求的g(x)的最小值時對應的自變量的值。
Python編程實踐非線性的最小二乘法
假如你需要求sin(x)=0.233時的x的取值。注意x的單位是弧度。雖然你可以調用Python代碼裏面的arcsin這個函數,但是爲何我們不試試自己編程實現arcsin呢?引用某知名網友的話“抱着造輪子的心態學東西(逃”。
我們整理下思路現在的最小二乘法問題就是我們需要求g(x) = |sin(x)-0.233|的最小值所在點的自變量值x。
在使用牛頓法前我們不要忘了牛頓法是有個假設的。那就是目標函數g(x)的最小值是0.顯然現在我們這個例子是滿足的。
第一步:隨便猜g(x) 最小值所在點對應的自變量值. 現在我們假設這個自變量值是。所以我們得到了一個點。而且知道這個點的導數值。然後我們可以寫出經過這個點的切線方程。所以切線方程爲
第二步:將切線看做是原函數的一個近似,然後令切線函數值y等於0來修正第一步的猜測值。根據可以解得。我們可以將這個字作爲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;
}