第五次上机作业上机报告

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

第四次上机作业报告

李声扬 2100011053 工学院

1.题目

要求编制使用 Newton 迭代法和最速下降法求解非线性方程组


1
𝑥 + 3𝑦 3 + 2𝑧 2 =
2
𝑥 + 3𝑦 + 𝑧 = 2 的程序。初值为𝑥 (0) = (−1, 0.1, 0.7)𝑇 .
2 3

3 2 1
{𝑥 + 2𝑦 + 2𝑧 = 2

2.数值计算方法分析

Ⅰ.Newton 迭代法

该方法本质是线性化的方法,将方程组中每个方程线性化得到一

个线性方程组,由此构造迭代格式求得方程组的近似解。具体来说:
由于求解逆矩阵会遇到算法难度大且精度损失的问题,采用 LU 分解:

重复上述过程直到满足精度要求
Ⅱ.最速下降法

该方法本质是将解非线性方程组问题化成优化问题,然后以最优

化方法求解。
我们会发现,最速下降法总是收敛的,并不依赖于初值,但缺点在于

收敛的速度较慢,仅为线性收敛,而牛顿迭代法的收敛性需要再初值

充分接近𝑥 ∗ 时,优点在于它是二阶收敛的,缺点在于牛顿迭代法每一

步都需要进行导数值的计算。有关两者收敛速度的关系我们能从程序

的求解中看出。

问题答案:

3.程序实现

注:由于且 C 语言并不好实现对于一般函数求偏导,且我的计算机水

平并不高所以我只采用就题论题的方式对特定非线性方程组求出了

答案,有关更一般非线性函数方程组求解的程序实现我在最后做出了

讨论。

Ⅰ.Newton 迭代法

按照题目给出的函数关系将偏导数的函数关系求出,带入每次迭代的

数值求出 Jacobi 矩阵并进行 LU 分解,最后依照第二节中的求解方法


求出近似解。
1. void f(int k){//该函数用于将自变量代入每个非线性方程得到非线性函数的值
2. y[k][0]=x[k][0]+3*pow(x[k][1],3)+2*pow(x[k][2],2)-0.5;
3. y[k][1]=pow(x[k][0],2)+3*x[k][1]+pow(x[k][2],3)-2;
4. y[k][2]=pow(x[k][0],3)+2*x[k][1]+2*pow(x[k][2],2)-0.5;
5. return;
6. }
7. void calculate_jacobi(int k){//该函数用于计算 Jacobi 矩阵
8. jacobi[0][0]=1;
9. jacobi[0][1]=9*pow(x[k][1],2);
10. jacobi[0][2]=4*x[k][2];
11. jacobi[1][0]=2*x[k][0];
12. jacobi[1][1]=3;
13. jacobi[1][2]=3*pow(x[k][2],2);
14. jacobi[2][0]=3*pow(x[k][0],2);
15. jacobi[2][1]=2;
16. jacobi[2][2]=4*x[k][2];
17. return;
18. }
19. void cal_L_and_U_matrix(int k){//该函数用于对 jacobi 矩阵进行 LU 分解
20. for(int i=0;i<3;i++) l[i][i]=1;
21. for(int j=0;j<3;j++) u[0][j]=jacobi[0][j];
22. for(int i=1;i<3;i++) l[i][0]=(double)(jacobi[i][0]/u[0][0]);
23. for(int j=1;j<3;j++) u[1][j]=jacobi[1][j]-l[1][0]*u[0][j];
24. l[2][1]=(double)((jacobi[2][1]-l[2][0]*u[0][1])/u[1][1]);
25. u[2][2]=jacobi[2][2]-l[2][1]*u[1][2]-l[2][0]*u[0][2];
26. return;
27. }

Ⅱ.最速下降法

求解下降梯度:将数值代入偏导数求出值即可
1. void solve_Descending_gradient(double *G,double *x,double *y){//该函数
用于求解下降梯度
2. G[0]=2*y[0]+(4*x[0])*y[1]+(6*pow(x[0],2))*y[2];
3. G[1]=(18*pow(x[1],2))*y[0]+6*y[1]+4*y[2];
4. G[2]=(8*x[2])*y[0]+(6*pow(x[2],2))*y[1]+(8*x[2])*y[2];
5. return;
6. }

求解最小步长,这一步是整个程序实现最困难的地方,如果采用更一

般的函数形式就更困难了,我这里由于函数给定了,采用的是对于步
长函数𝜑(𝜆)使用牛顿迭代法求其一阶导数的零点。

具体程序为:
1. double min_a(int k){//该函数用于求解最小步长,这里我采用的是使用牛顿法求步
长函数一阶导的零点
2. double a=0,b=0;
3. while(1){
4. a=b;//b 只是用来方便写程序的中间量
5. for(int j=0;j<3;j++) d[k][j]=x[k][j]-a*G[k][j];
6. f(d[k],z[k]);
7. double der_1[3],der_2[3];//der_i 代表 f1,f2,f3 关于步长 a 的 i 阶
导的值
8. double F=0,F_der=0;//用来方便求和的中间两
9. der_1[0]=-G[k][0]-(9*pow(d[k][1],2))*G[k][1]-
(4*d[k][2])*G[k][2];
10. der_1[2]=-(3*pow(d[k][0],2))*G[k][0]-2*G[k][1]-
(4*d[k][2])*G[k][2];
11. der_1[1]=-(2*d[k][0])*G[k][0]-3*G[k][1]-
(3*pow(d[k][2],2))*G[k][2];
12. der_2[0]=(18*pow(G[k][1],2))*d[k][1]+4*pow(G[k][2],2);
13. der_2[1]=2*pow(G[k][0],2)+(6*pow(G[k][2],2))*d[k][2];
14. der_2[2]=(6*pow(G[k][0],2))*d[k][0]+4*pow(G[k][2],2);
15. for(int i=0;i<3;i++){
16. F+=z[k][i]*der_1[i];
17. F_der+=pow(der_1[i],2)+z[k][i]*der_2[i];
18. }
19. b=a-(double) (F/F_der);
20. if(fabs(b-a)<1e-8){
21. return b;
22. }
23. }
24. }

4.关于程序优化的思考

事实上,如果要写对于一般形式的非线性函数使用 c 语言并不合适,

但也不是没有办法。对于具有超越结构的函数我们先不讨论,对于多

项式函数我们可以设出组成每一个函数的单项式个数,而单项式由常

系数,变量次数可以唯一确定,同时每个单项式确定后其偏导形式上

也就确定了。采用这种设出函数型的方式可以是程序适应于更一般的

非线性函数。

5.分析与思考

我们观察最终的求解结果:牛顿法仅用四次就达到了精度 1e-8,而最

速下降法需要 121 次,这是因为某点的负梯度方向,通常只是在该点

附近才具有这种最速下降的性质。最速下降法利用目标函数一阶梯度

进行下降求解,易产生锯齿现象(如下图),在开始几步,目标函数

下降较快;但在接近极小点时,收敛速度长久不理想了。特别适当目

标函数的等值线为比较扁平的椭圆时,收敛就更慢了。因此,在实用

中常用最速下降法和其他方法联合应用,在前期使用最速下降法,而

在接近极小值点时,可改用收敛较快的其他方法。
再结合牛顿法需要初值在准确解附近才有较好的收敛性,在初值不那

么好的情况下,我们可以先用最速下降法求出比较精确的初值,在使

用牛顿迭代法求出精确度高的解。

You might also like