玩轉matlab之一維 gauss 數值積分公式及matlab原始碼

孫振威發表於2019-05-10

在數值分析中,尤其是有限元剛度矩陣、質量矩陣等的計算中,必然要求如下定積分:
\[ I=\int_a^b f(x)dx \]學好gauss積分也是學好有限元的重要基礎,學過高等數學的都知道,手動積分能把人搞死(微笑臉),而且有些函式還不存在原函式,使用原始的手動算出原函式幾乎是不現實的。因此非常有必要學習數值積分,簡單講就是近似計算,只要這個近似值精確度高和穩定性好就行。Gauss積分公式就是這麼一個非常好用的工具。本文介紹高斯積分公式的使用以及簡單的數值算例。

標準區間

先考慮特殊情況,對於一般區間呢?待會會處理這個問題。
\[ I=\int_{-1}^1 f(x)dx \]
不加證明的直接給出gauss公式如下:詳情參閱任何一本數值分析書都有詳細的證明過程:
\[ I=\int_{-1}^1 f(x)dx=\Sigma_{i=1}^n A_if(x_i) \]
其中\(A_i\)稱作\(x_i\)稱作 gauss 點
下面的問題就是如何選擇\(n,A_i,x_i\)

理論表明n個點的Gauss公式代數精度為\(2n-1\),其選擇如下表,(這裡僅僅舉1-4個點情況,實際使用的時候一般2點或者3點的精度已經完全夠了)更多積分點可參考 gauss表.

gauss點個數 \(n\) gauss 點 \(x_i\) 權重 \(A_i\) 精度
1 \(x_1\)=0 \(A_1\)=2 1
2 \(x_{1,2}=\pm1/\sqrt{3}\) \(A_1=A_2=1\) 3
3 \(x_1=-\sqrt{3/5}\)
\(x_2=0\)
\(x_3=\sqrt{3/5}\)
\(A_1=5/9\)
\(A_2=8/9\)
\(A_3=5/9\)
5
4 \(x_{1}=-\sqrt{\dfrac{15+2\sqrt{30}}{35}}\)
\(x_{2}=-\sqrt{\dfrac{15-2\sqrt{30}}{35}}\)
\(x_{3}=\sqrt{\dfrac{15-2\sqrt{30}}{35}}\)
\(x_{4}=\sqrt{\dfrac{15+2\sqrt{30}}{35}}\)
\(A_1=\frac{90-5\sqrt{30}}{180}\)
\(A_2=\frac{90+5\sqrt{30}}{180}\)
\(A_3=\frac{90+5\sqrt{30}}{180}\)
\(A_4=\frac{90-5\sqrt{30}}{180}\)
7

一般區間

\[ I=\int_a^b f(x)dx \]

根據上面的討論情況,可知只要做變換(相當於換元積分一樣)
\[ 令\quad x=\frac{b+a+(b-a)s}{2},\\ 則\quad dx = \frac{b-a}{2}ds. \]
那麼有\(s\in[-1,1]\),於是即可使用標準區間公式如下:
\[ I = \int_a^bf(x)dx=\int_{-1}^1f(\frac{b+a+(b-a)s}{2})\times\frac{b-a}{2}ds\\ =\frac{b-a}{2}\Sigma_{i=1}^mA_if(\frac{b+a+(b-a)s_i}{2}) \]
上述公式中的\(A_i\)即為表格中的權重,\(s_i\)即為上表中對應的gauss點,代入公式即可計算積分值。

數值實驗

所有實驗在MATLAB2018a版本下完成。(建議安裝新版本,因為很多函式在新版中已經優化了或者改名字了,比如老版本積分函式quad 新版已經改為integral,只不過目前quad函式還是可以使用的,將來會被刪除)。

我們取2個函式做實驗,分別計算出其gauss積分值再與matlab自帶的函式 integral 計算結果作比較,實驗模型是:
\[ 計算 \quad I= \int_1^2 f(x)dx \]

實驗一

取函式
\[ f(x)=lnx, \quad 即自然對數函式以e為底. \]
使用matlab函式 integral 計算得到: \(I= 0.386294361119891\)
使用gauss積分的matlab計算結果為:

高斯點數 m 積分值 \(I_m\) 誤差norm(\(I_m-I\))
2 0.386594944116741 3.01E-04
3 0.386300421584011 6.06E-06
4 0.386294496938714 1.36E-07
5 0.386294364348948 3.23E-09

實驗二

取函式
\[ f(x)=\dfrac{x^2+2x+1}{1+(1+x)^4}, \]
使用matlab函式 integral 計算得到: \(I= 0.161442165779443\)
使用gauss積分的matlab計算結果為:

高斯點數 m 積分值 \(I_m\) 誤差norm(\(I_m-I\))
2 0.161394581386268 4.76E-05
3 0.161442818737102 6.53E-07
4 0.161442196720137 3.09E-08
5 0.161442166345131 5.66E-10

總結

  1. 隨著gauss點m的個數增多,精度在逐漸提高,但是要注意的是,gauss點取得多的話,計算量也會增大,只是因為我們計算的問題規模比較小,所以感覺不到而已。
  2. 另外可以看到2點3點的gauss公式的精度已經很高了,說明並不需要取太多的點,而在實際計算中,比如有限元的計算中,也僅僅取2點或者3點gauss積分就完全足夠。

下節預告

下次介紹gauss積分的二維公式使用以及matlab數值實驗,歡迎有問題與我交流,偏微分方程,矩陣計算,數值分析等問題,我的qq 群 315241287

matlab程式碼

clc;clear;
%   using 2 3 4 5 points compute the integral
%   x \in [a,b]
%
%%  test
a=1;    b = 2;
fun = @(x)  log(x);
% fun = @(x)  2*x./(1+x.^4);
% fun = @(x)  exp(-x.^2/2);
% fun = @(x)  (x.^2+2*x+1)./(1+(1+x).^4);
%%  setup the gauss data
for gauss = 2:5
    if gauss == 2
        s=[-1 1]/sqrt(3);
        wt=[1 1];
        fprintf('***************************  2     points gauss  *******')
    elseif gauss == 3
        s = [-sqrt(3/5) 0 sqrt(3/5)];
        wt = [5 8 5]/9;
        fprintf('***************************    3   points gauss  *******')
    elseif gauss == 4
        fprintf('***************************    4   points gauss  *******')
        s = [   -sqrt((15+2*sqrt(30))/35),  -sqrt((15-2*sqrt(30))/35), ...
            sqrt((15-2*sqrt(30))/35),   sqrt((15+2*sqrt(30))/35)];
        wt = [  (90-5*sqrt(30))/180,    (90+5*sqrt(30))/180,...
            (90+5*sqrt(30))/180,    (90-5*sqrt(30))/180];
    elseif gauss == 5
        fprintf('***************************    5    points gauss *******')
        s(1)=.906179845938664 ; s(2)=.538469310105683;
        s(3)=.0;      s(4)=-s(2) ; s(5)=-s(1);
        wt(1)=.236926885056189 ; wt(2)=.478628670499366;
        wt(3)=.568888888888889 ; wt(4)=wt(2) ; wt(5)=wt(1);
    end
    %%
    %   區間變換到   s \in[-1,1]
    s = (b-a)/2*s+(b+a)/2;
    jac = (b-a)/2;% dx = jac * ds
    f = fun(s);
    f = wt.* f .* jac;
    format long
    exact = integral(fun,a,b);
    comp = sum(f)
    fprintf('the error is norm(comp-exact)=%10.6e\n\n\n',norm(comp-exact))
end
fprintf('\n\n*********  matlab  built-in function ''integral''*********\n')
exact = integral(fun,a,b)
format short

相關文章