數值分析:非線性方程組求解

前言

非線性方程組,顧名思義就是未知數的冪除了不是1,其他都有可能!線性方程組其實只是非常小的一類,非線性方程組才是大類!也正因此非線性方程組包含各種各樣的方程形式,所以它的解和對應的求解方法不可能像線性方程組那樣完美,即都是局部收斂的。先給出一個直觀的非線性方程組例子:

\begin{cases} x_{1}^{2} - 10*x_{1} + x_{2}^{2} + 8 = 0 & \\ x_{1}*x_{2}^{2} + x_{1} - 10*x_{2} + 8 = 0 & \end{cases}


個人對兩個問題的理解:

1. 非線性方程組如果有解,一般都有很多解!如何理解:

把方程組的看成是各個函數圖像交點的。我們知道非線性方程組的各個函數就都是復雜曲線/面,甚至是高緯空間里的復雜東西;線性方程組的各個函數就是最簡單的直線/面!各個復雜函數圖像間的相交機會很多,并且只要相交,就是多個交點(因為交線、交面里有無數的交點),也就是有多個解~

可以想象,非線性方程組有多解是很平常的一件事~ 對于復雜的非線性函數沒解才不正常!
可以想象,這些解是等價的!沒有說是等級更高,誰等級低一些。都是解!因為:只要是解,它就只滿足一個條件:讓方程組中的各個方程=0。所以無法用什么評判標準(比如范數)來說哪個解的等級高一些或者效果更好一些。
注意:這里的解等價欠定線性方程組通解中的唯一極小范數解不一樣!可以想象二者的區別:非線性方程組中的解都是實打實存在的;而欠定線性方程組中除了特解,其他通解中的解說存在也行,說不存在那就是因為方程條件(個數)都不夠!這些是啥都行的通解和非線性方程組中實打實存在的解肯定不能比!

這樣的話各個非線性方程組的局部收斂性就可以理解,即:空間中有很多解時,我每次只能找一個,那我找誰?找離我出發點最近的那個解唄。所以不同的出發點,就有可能找到不同的解,這就是局部收斂性。

2. 為什么不能用簡單的替換先變成線性方程組求解,然后再解每一個非線性方程?

意思是:每個方程中把所有和x_1有關的用一個變量代替,所有x_2有關的用一個變量代替,即方程1中用:w_1 = x_{1}^{2} - 10*x_{1},w_2 = x_{2}^{2} + 8
但是很明顯方程2的第一項x_{1}*x_{2}^{2}兩個變量相乘,沒法用變量代替~
并且,即使在方程2中能代替,那么就會有w_3w_4,這樣總未知數變成4個方程只有2個,還是解不了。
所以,非線性方程組不可能用簡單的線性變量代換來解。


本文介紹最常用、最好用的非線性方程組解法,包括牛頓法擬牛頓法(割線法)兩大類:

  • 牛頓法:原始牛頓法、修正牛頓法;
  • 擬牛頓法:逆Broyden秩1法、逆Broyden秩1第二公式、BFS秩2法;

上面這5種方法的函數表達式、計算流程、代碼都會詳細說明和展示。并且,這5種方法都很經濟(算的快)、實用(適用各種非刁鉆的函數)、易用(計算流程好理解,便于編程)。

本文側重的是方法的使用,不提推導和太具體的其他細節。

非線性方程組解法 —— 牛頓法

本文要介紹的5種方法可分為兩大類:牛頓法、擬牛頓法。
先把兩類方法的優缺點列出來:

牛頓法:用jacobi矩陣(導數)

  • 優點:導數法收斂速度巨快(平方收斂);自校正誤差不會傳遞;
  • 缺點:求導稍費事;只要賦值后的jacobi矩陣存在稀疏性、奇異性、病態等,就跪了;

擬牛頓法:割線法思想,用近似矩陣趨近jacobi矩陣

  • 優點:jacobi矩陣的問題在這里都不是問題!這個優點極大提高解法的穩定性?。?!
  • 缺點:收斂速度介于平方收斂和直線收斂之間,稍慢一丟丟。

其實,牛頓法看上去要求導很麻煩,其實導數就求了一次而已!剩下都是在循環里對jacobi矩陣的賦值!所以去求導其實不是大問題。大問題就是每次賦值后的jacobi矩陣要求逆?。∵@就對數值矩陣的要求很高了!并且實際問題中經常出現賦值后的jacobi矩陣是稀疏的。
所以,如果原函數們不刁鉆,兩種方法都可勝任。但如果稍微感覺原函數有些復雜時,建議犧牲一丟的速度選擇擬牛頓法!擬牛頓法的穩定性真的提高一個量級。

下面我們將正式開始方法的介紹,給定一個n\times n的非線性方程組的通式:

F(x) = [f_1(x),f_2(x),\cdots,f_n(x)]^{T} = 0

對應的jacobi矩陣為:

F^{'}(x) = \left( \begin{matrix} \frac{\partial f_1}{\partial x_1} & \frac{\partial f_1}{\partial x_2} & \cdots & \frac{\partial f_1}{\partial x_n} \\ \frac{\partial f_2}{\partial x_1} & \frac{\partial f_2}{\partial x_2} & \cdots & \frac{\partial f_2}{\partial x_n} \\ \vdots & \vdots & & \vdots \\ \frac{\partial f_n}{\partial x_1} & \frac{\partial f_n}{\partial x_2} & \cdots & \frac{\partial f_n}{\partial x_n} \end{matrix} \right)

原始牛頓法

x_0自己給定,然后進行迭代:

x^{k+1} = x^{k} - [F^{'}(x^{k})]^{-1}F(x^k)

上述迭代可以用了,但是每次循環都要求逆很慢!所以換成下面這種形式:

\begin{cases} x^{k+1} = x^{k} + \Delta x^{k} & 正常迭代\\ F^{'}(x^{k})\Delta x^{k} + F(x^k) = 0 & 每次解這個線性方程組 \end{cases}

設真實解為x^{*},實現流程如下:


  • 步1:給出初始近似x_0及計算精度\varepsilon _1\varepsilon _2;

  • 步2:假定已進行了k次迭代,已求出x^{k}F(x^{k}),計算F^{'}(x^{k})并做賦值:
    A_k = F^{'}(x^{k}) \quad ;\quad b_k = F(x^{k})

  • 步3:解上面的線性方程組(用預處理后萬能高斯-賽德爾迭代):
    F^{'}(x^{k})\Delta x^{k} + F(x^k) = 0

  • 步4:求下面兩個式子:
    x^{k+1} = x^{k} + \Delta x^{k} \quad ;\quad F(x^{k+1})

  • 步5:若\parallel \Delta x^{k} \parallel < \varepsilon _1\parallel F(x^{k+1}) \parallel < \varepsilon _2,則置:
    x^{*} = x^{k+1}
    轉步6;否則進行如下賦值后回到步2
    k = k+1 \quad ;\quad x^{k} = x^{k+1} \quad ;\quad F(x^{k}) = F(x^{k+1})

  • 步6:結束。


針對最開始的例子,下面給出matlab程序:

clear ; clc

% 未知數: 
syms x1 x2;

% 方程組: 統一用列向量
f1 = x1^2 - 10*x1 + x2^2 + 8;
f2 = x1*x2^2 + x1 - 10*x2 + 8;
% f1 = 4*x1 - x2 + 0.1*exp(x1)-1;
% f2 = -x1 + 4*x2 + 1/8*x1^2;
x = [x1;x2];
f = [f1;f2];

% 初始值: 統一用列向量
x0 = [2;3];
error_dxk = double( input('dxk范數的精度:') );
error_fkk = double( input('fkk范數的精度:') );
num = input('停止迭代次數:');

% jacobi1 = [diff(f1,x1) diff(f1,x2);diff(f2,x1) diff(f2,x2)]
% 直接用自帶函數求雅克比矩陣:
jacobi = jacobian([f1,f2],[x1,x2]);

for k = 1:num
    Ak = double( subs(jacobi, x, x0) );
    bk = double( subs(f, x, x0) );
    dxk = pre_seidel(Ak,-bk,k);  % 步長
    x0 = x0 + dxk;
    fkk = double( subs(f, x, x0) );  % fk+1單純用來判斷
    if norm(dxk) < error_dxk | norm(fkk) < error_fkk
        break;
    end
end

if k < num
    x_result = x0;
    fprintf('精度已達要求,迭代提前結束!\n');
    fprintf('%d次迭代后, 近似解為:\n',k);
    x_result
else
    x_result = x0;
    fprintf('迭代次數已達上限!\n');
    fprintf('似解為:\n',k);
    x_result
end
    
fprintf('f1結果為:%f\n',double( subs(f1,x,x0) ));
fprintf('f2結果為:%f\n',double( subs(f2,x,x0) ));
fprintf('dxk范數:%f\n',norm(dxk));
fprintf('fkk范數:%f\n',norm(fkk));

結果:

dxk范數的精度:0.0001
fkk范數的精度:0.0001
停止迭代次數:200
第1次求解線性方程組, 當前萬能賽德爾迭代矩陣譜半徑為(越小越好): 0.8306
第2次求解線性方程組, 當前萬能賽德爾迭代矩陣譜半徑為(越小越好): 0.0668
第3次求解線性方程組, 當前萬能賽德爾迭代矩陣譜半徑為(越小越好): 0.1969
第4次求解線性方程組, 當前萬能賽德爾迭代矩陣譜半徑為(越小越好): 0.2209
第5次求解線性方程組, 當前萬能賽德爾迭代矩陣譜半徑為(越小越好): 0.2214
精度已達要求,迭代提前結束!
5次迭代后, 近似解為:

x_result =

    0.9999
    1.0000

f1結果為:0.000728
f2結果為:0.000184
dxk范數:0.000000
fkk范數:0.000751

說明:完全按照上面的流程編寫的程序,非常好理解。
對應的pre_seidel自定義函數在下載地址里面都有。

修正牛頓法

對于上面的原始牛頓法,如果每步計算F^{'}(x^{k})改為固定的F^{'}(x^{0})可得:

x^{k+1} = x^{k} - [F^{'}(x^{0})]^{-1}F(x^{k}) = x^{k} - BF(x^{k})

這樣就變成了"簡化牛頓法"。很明顯可以看到簡化牛頓法是線性收斂的!
計算量小,但是收斂速度非常慢。

如果既擁有"原始牛頓法"的收斂速度,又擁有"簡化牛頓法"的工作量???—— 修正牛頓法。

對應的迭代格式為:

\begin{cases} x^{k,0} = x^{k} & \\ x^{k,i} = x^{k,i-1} - [F^{'}(x^{k})]^{-1}\color{red}{F(x^{k,i-1})} & i=1,\cdots,m ; k = 1,\cdots,n\\ x^{k+1} = x^{k,m} & 條件3 \end{cases}

從迭代公式可以得知,在每一個k的大循環內都有一個從1到m的小循環來求x^{k,m},下面同樣給出實現流程:


  • 步1:給出初始近似x_0及計算精度\varepsilon _1\varepsilon _2;

  • 步2:根據方程階數/個數n,求出效率最大化的m值或人為給定一個m值;

  • 步3:假定已進行k此迭代,已算出x^{k}F(x^{k}),計算并賦值:

F^{'}(x^{k}) \quad ;\quad A_{k} = [F^{'}(x^{k})]^{-1}

  • \color{red}{步4(小循環)}:進入每次的內層循環,假設已內層循環i次,已算出x^{k,i}F(x^{k,i}),先做1個賦值,再做2個計算:

b = F(x^{k,i}) \quad ;\quad x^{k,i+1} = x^{k,i} - A_kb \quad ;\quad F(x^{k,i+1})

  • \color{red}{步5(小循環)}:先做3個賦值

i = i+1 \quad ;\quad x^{k,i} = x^{k,i+1} \quad ;\quad F(x^{k,i}) = F(x^{k,i+1})

然后做一個計算:

z = A*b

然后再一次判斷:如果i == \color{red}{m},轉到步6(出小循環),否則回到步4(沒出小循環);

  • 步6:若\parallel z \parallel < \varepsilon _1 \parallel F(x^{k,\color{red}{m}}) \parallel < \varepsilon _2,則賦值并結束:

x^{*} = x^{k,m}

否則,k=k+1然后重回步3(大循環)。


仍針對最開始的例子,給出matlab程序:

clear ; clc;

% 未知數: 
syms x1 x2;

% 方程組: 統一用列向量
f1 = x1^2 - 10*x1 + x2^2 + 8;
f2 = x1*x2^2 + x1 - 10*x2 + 8;
x = [x1;x2];
f = [f1;f2];

% 初始值: 統一用列向量
x0 = [5;4];
error_z = double( input('z范數的精度:') );
error_fk = double( input('fk范數的精度:') );
num = input('停止迭代次數:');

% 直接用自帶函數求雅克比矩陣:
jacobi = jacobian([f1,f2],[x1,x2]);

% 小循環m取多少的判斷: 和方程個數N有關,求w的最大值
syms M N;
mn0 = [N;M];
% 參數xzn是方程的個數:
xzn = length(x);  
% 原始的效率對比方程:
w = (N+1)*log(M+1)/( (N+M)*log(2) ); 
% 讓w方程求最大值的xzm值:
xzm = double( solve( diff( subs(w,N,xzn),M ) ) );
mn1 = [xzn;xzm];
% 最高效率值:
wax = double( subs(w,mn0,mn1) );

% 開始修正牛頓迭代:
xki = x0;
for k = 1:num
    fk = double( subs(f,x,x0) );
    Ak = inv( double( subs(jacobi, x, x0) ) );
    for m = 1:round(xzm)
        b = fk;
        x0 = x0 - Ak*b;
        fki = double( subs(f,x,x0) );
        fk = fki;
        z = Ak*b;    
    end
    if norm(z) < error_z | norm(fk) < error_fk
        break;
    end
end

if k < num
    x_result = x0;
    fprintf('精度已達要求,迭代提前結束!\n');
    fprintf('%d次迭代后, 近似解為:\n',k);
    x_result
else
    x_result = x0;
    fprintf('迭代次數已達上限!\n');
    fprintf('似解為:\n',k);
    x_result
end

fprintf('f1結果為:%f\n',double( subs(f1,x,x0) ));
fprintf('f2結果為:%f\n',double( subs(f2,x,x0) ));
fprintf('z范數:%f\n',norm(z));
fprintf('fk范數:%f\n',norm(fk));

效果和原始牛頓法一樣,可以感覺到速度提升了!!
這里用到了求最效率的m值,下面對它的求法加以補充(完全可以不求手動給):

修正牛頓法N_{x}原始牛頓法N_{y}的效率之比為:

\frac{e(N_{x})}{e(N_{y})} = \frac{n+1}{n+m}\frac{ln^{(m+1)}}{ln^{2}}

其中n就是方程組中方程的個數,這對于每個方程組都是固定常數。所以要想效率最高,那就讓上式中右邊含m參數的表達式取最大值即可(求導讓導函數=0),即如程序中所示。m求出若不是整數,就4舍5入。

至此,牛頓法的兩種方法介紹完畢。下面簡單對比一些二者的不同:

? 原始牛頓法 修正牛頓法
區別 不求jacobi矩陣的逆
每次要求線性方程組
不求線性方程組
每次要求jacobi的逆

前文已經說明:每次要對矩陣求逆的話就很不穩定!
所以:其實原始牛頓法不錯!好編程、速度快、不求逆、線性方程組有萬能解法!修正牛頓法單純提高了一丟丟效率,但是穩定性個人覺得變差了。
故,牛頓法中我推薦使用原始牛頓法。

非線性方程組解法 —— 擬牛頓法

牛頓法中要求導的jacobi矩陣,自然可以想到能否用差商來代替求導
肯定是可以的,這就是割線法的思想,
即牛頓法是用超切平面趨近,割線法是用超割平面去趨近。
常用的割線法有:2點割線法、n點割線法、n+1點割線法;(n是方程個數)
其中n+1點割線法效率是最高的,但是穩定性沒有n點割線法好!

所以,能不能構造一種算法,它具備n+1點割線法效率高的優點同時又增加穩定性?
這就是擬牛頓法
所以:擬牛頓法是基于割線法,并做的比割線法更好的算法!
所以:擬牛頓法和割線法的"收斂速度"介于線性收斂和平方收斂之間;
所以:本文就不介紹割線法怎么搞,直接上最好的割線法 —— 擬牛頓法。


擬牛頓法中最好的是Broyden提出的操作,統稱為Broyden方法。
思想:不斷用一個低秩矩陣A_k進行修正;不同方法的區別就是那個低秩矩陣不同~
可以想象:低秩矩陣不同的秩數,就能得到一系列方法。
最常用:Broyden秩1、Broyden秩1第二方法、逆Broyden秩1,逆Broyden秩1第二方法。
另外還有秩2的方法,比如比較好的BFS。

秩1相關方法

1. Broyden秩1迭代公式:

\begin{cases} x^{k+1} = x^{k} - A_{k}^{-1}F(x^{k}) & \\ A_{k+1} = A_{k} + \frac{ (y_{k} - A_{k}s_{k})(s_{k})^{T} }{ (s_{k})^{T}s_{k} } & \end{cases}

其中:s_{k} = x^{k+1} - x^{k} \quad ;\quad y_{k} = F(x^{k+1}) - F(x^{k})

2. Broyden秩1第二方法迭代公式:

\begin{cases} x^{k+1} = x^{k} - A_{k}^{-1}F(x^{k}) & \\ A_{k+1} = A_{k} + \frac{ F(x^{k+1}) F(x^{k+1})^{T} }{ F(x^{k+1})^{T} (x^{k+1} - x^{k}) } & \end{cases}

上面的兩種方法中的第一個式子都要求逆,不好看!我們用B_k = A_{k}^{-1}代替,然后對上面的兩個方法分別做改寫,就可以得到它們的逆方法!

3. 逆Broyden秩1迭代公式:√

\begin{cases} x^{k+1} = x^{k} - B_{k}F(x^{k}) & \\ B_{k+1} = B_{k} + \frac{ (s_{k} - B_{k}y_{k})(s_{k})^{T}B_{k} }{ (s_{k})^{T}B_{k}y_{k} } & \end{cases}

4. 逆Broyden秩1第二方法迭代公式:√

\begin{cases} x^{k+1} = x^{k} - B_{k}F(x^{k}) & \\ B_{k+1} = B_{k} + (s_{k} - B_{k}y_{k})\frac{ (s_{k} - B_{k}y_{k})^{T} }{(s_{k} - B_{k}y_{k})^{T}y_{k} } & \end{cases}


對于上面4個方法/迭代公式,有下面3點說明:

  • 文獻中說"第二方法"類只適用于jacobi矩陣對稱!但其實不對稱的時候也可以用!不過穩定性大幅變差;
  • "逆方法"類的穩定性會提高!所以實際中/本文使用逆方法類。
  • 方法間的區別只在第2個公式中右邊那個項。故編程只用換那個表達式即可。

下面給出"逆Broyden秩1方法"實現流程:

  • 步1:給出初始近似x_0及計算精度\varepsilon _1\varepsilon _2;

  • 步2:計算初始矩陣B_{0},一般?。?/p>

B_{0} = [F^{'}(x^{0})]^{-1}

  • 步3:k = 0;計算F(x^{0})

  • 步4:計算s_{k}x_{k+1}

s_{k} = -B_{k}F(x^{k}) \quad ;\quad x^{k+1} = x^{k} + s_{k}

  • 步5:計算F(x^{k+1});檢驗若若\parallel s_{k} \parallel < \varepsilon _1 \parallel F(x^{k+1}) \parallel < \varepsilon _2則轉步8,否則轉步6;

  • 步6:計算y_{k},并根據上面公式計算B_{k+1}

y_{k} = F(x^{k+1}) - F(x^{k})

B_{k+1} = B_{k} + \frac{ (s_{k} - B_{k}y_{k})(s_{k})^{T}B_{k} }{ (s_{k})^{T}B_{k}y_{k} }

  • 步7:做4個賦值,然后回到步4

k = k+1 \quad ;\quad F(x^{k}) = F(x^{k+1}) \quad ;\quad B_{k} = B_{k+1} \quad ;\quad x^{k} = x^{k+1}

  • 步8:x^{*} = x^{k+1},結束。

仍對于最開始的例子,給出逆Broyden秩1法的matlab程序:

clear; clc;

% 未知數: 
syms x1 x2;

% 方程組: 統一用列向量
f1 = x1^2 - 10*x1 + x2^2 + 8;
f2 = x1*x2^2 + x1 - 10*x2 + 8;
x = [x1;x2];
f = [f1;f2];

% 初始值: 統一用列向量
x0 = [2;3];
error_sk = double( input('sk范數的精度:') );
error_fkk = double( input('fkk范數的精度:') );
num = input('停止迭代次數:');

% 直接用自帶函數求雅克比矩陣:
jacobi = jacobian([f1,f2],[x1,x2]);

% 開始求解前的初值:
Bk = inv( double( subs(jacobi,x,x0) ) );
fk = double( subs(f,x,x0) );
% 循環完全按照流程來
for k = 1:num
    sk = -Bk*fk;
    x0 = x0 + sk;
    fkk = double( subs(f,x,x0) );
    if norm(sk) < error_sk | norm(fkk) < error_fkk 
        break;
    end
    yk = fkk - fk;
    Bkk = Bk + (sk-Bk*yk)*(sk')*Bk/( sk'*Bk*yk );  % 不同方法改這里表達式即可
    fk = fkk;
    Bk = Bkk;
end

if k < num
    x_result = x0;
    fprintf('精度已達要求,迭代提前結束!\n');
    fprintf('%d次迭代后, 近似解為:\n',k);
    x_result
else
    x_result = x0;
    fprintf('迭代次數已達上限!\n');
    fprintf('似解為:\n',k);
    x_result
end

fprintf('f1結果為:%f\n',double( subs(f1,x,x0) ));
fprintf('f2結果為:%f\n',double( subs(f2,x,x0) ));
fprintf('sk范數:%f\n',norm(sk));
fprintf('fkk范數:%f\n',norm(fkk));

效果就是比牛頓法多迭代幾次而已。
對應的逆Broyden秩1第二方法只用改程序中的Bkk表達式即可~ 故不多展示。

秩2相關方法

個人感覺:秩越高,"局部收斂"的范圍更廣一些(是好事)!后面會舉例子說明這一點。

個人感覺穩定、效率高、效果好的秩2方法是:BFS(Broyden-Fletcher-Shanme),迭代公式為:

\begin{cases} x^{k+1} = x^{k} - B_{k}F(x^{k}) & \\ B_{k+1} = B_{k} + \frac{\mu _{k}s_{k}(s_{k})^{T} - s_{k}(y_{k})^{T}B_{k} - B_{k}y_{k}(s_{k})^{T}}{(s_{k})^{T}y_{k}}& \end{cases}

其中\mu _{k}的表達式為:

\mu _{k} = 1 + \frac{(y_k)^{T}B_{k}y_{k}}{(s_{k})^{T}y_{k}}

實現上和秩1法基本上就是一樣的,只不過每次循環多算一個\mu _{k}就行。給出程序:

clear; clc;

% 未知數: 
syms x1 x2;

% 方程組: 統一用列向量
f1 = x1^2 - 10*x1 + x2^2 + 8;
f2 = x1*x2^2 + x1 - 10*x2 + 8;
x = [x1;x2];
f = [f1;f2];

% 初始值: 統一用列向量
x0 = [5;4];
error_sk = double( input('sk范數的精度:') );
error_fkk = double( input('fkk范數的精度:') );
num = input('停止迭代次數:');

% 直接用自帶函數求雅克比矩陣:
jacobi = jacobian([f1,f2],[x1,x2]);

% 開始求解前的初值:
Bk = inv( double( subs(jacobi,x,x0) ) );
fk = double( subs(f,x,x0) );

for k = 1:num
    sk = -Bk*fk;
    x0 = x0 + sk;
    fkk = double( subs(f,x,x0) );
    if norm(sk) < error_sk | norm(fkk) < error_fkk 
        break;
    end
    yk = fkk - fk;
    miuk = 1 + yk'*Bk*yk/(sk'*yk);   % 就多算一個這個而已
    Bkk = Bk + (miuk*sk*sk' - sk*yk'*Bk - Bk*yk*sk')/(sk'*yk);
    fk = fkk;
    Bk = Bkk;
end

if k < num
    x_result = x0;
    fprintf('精度已達要求,迭代提前結束!\n');
    fprintf('%d次迭代后, 近似解為:\n',k);
    x_result
else
    x_result = x0;
    fprintf('迭代次數已達上限!\n');
    fprintf('似解為:\n',k);
    x_result
end

fprintf('f1結果為:%f\n',double( subs(f1,x,x0) ));
fprintf('f2結果為:%f\n',double( subs(f2,x,x0) ));
fprintf('sk范數:%f\n',norm(sk));
fprintf('fkk范數:%f\n',norm(fkk));

至此,所有的方法就介紹完畢了。

總結

5種方法都在上面介紹并給出了程序,下面還有3點自己的心得體會(不一定正確!)。

1. 關于兩套精度判斷:\varepsilon _1\varepsilon _2
不同方法衡量的對象稍有不同,大體可歸納為:
\varepsilon _1衡量是前后兩次解的差值的范數;\varepsilon _2衡量是矩陣數值的范數;
可以認為是2套獨立的衡量標準,只不過我們在線性方程組里常用的是第一種而已罷了;
只要迭代在收斂,這兩個范數都是在不斷縮小的往0趨近的!故其實都可設置成0.0001這種形式;
誰先到,意味著有一個衡量標準已經達到了,就可以結束了!
當然讓兩個都達到了再結束也可以。一句話:只要迭代在收斂,兩套范數標準都是在下降趨向0的。

2. 擬牛頓法秩越高的方法"局部收斂"的范圍會廣一些!
對應上面同樣的例子:\varepsilon _1\varepsilon _2都是0.0001;5種方法的初值都是[5;4]時:
只有BFS這個秩2方法的最終解是[1;1]
其他4種方法全找的是[2.1934;3.0205]
雖然這兩個都是非線性方程組的解,但是為什么只有秩2的方法能搜到離起始點較遠的解?
個人猜測是:秩2法的"局部收斂"范圍比秩1法更廣!
但不管是秩幾,只要是牛頓法的大類(牛頓+割線+擬牛頓),都是局部收斂的

3. 使用推薦
若方程組很大,看重求解速度:使用原始牛頓法;
若看重求解的穩定性:擬牛頓法BFS秩2;

補充說明:牛頓法最有可能出問題的地方就是jacobi矩陣每次賦完值之后的數值矩陣!穩定性太依賴這個導函數矩陣。擬牛頓法穩定性非常好!并且個人感覺:秩越高穩定性越好、收斂速度越趨于平方收斂!

本文所有程序下載地址

參考寶書

  1. 李慶揚, 莫孜中, 祁力群. 非線性方程組的數值解法[M]. 科學出版社, 2005.
  2. 范金燕, 袁亞湘. 非線性方程組數值方法[M]. 科學出版社, 2018.
  3. 李星. 數值分析[M]. 科學出版社, 2014.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。