knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板

欢迎访问https://blog.csdn.net/lxt_Lucia~~
宇宙第一小仙女\(^o^)/~~萌量爆表求带飞=≡Σ((( つ^o^)つ~ dalao们点个关注呗~~

一.迭代法解方程 ( 组 ) 的根
本篇一、二部分转自“星博”:https://blog.csdn.net/Akatsuki__Itachi/article/details/80719686

首先,迭代法解方程的实质是按照下列步骤构造一个序列x0,x1,…,xn,来逐步逼近方程f(x)=0的解:
1)选取适当的初值x0;
2)确定迭代格式,即建立迭代关系,需要将方程f(x)=0改写为x=φ(x)的等价形式;
3)构造序列x0,x1,……,xn,即先求得x1=φ(x0),再求x2=φ(x1),……如此反复迭代,就得到一个数列x0, x1,……,xn,若这个数列收敛,即存在极值,且函数 φ(x)连续,则很容易得到这个极限值knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片
,x*就是方程f(x)=0的根。

举个例子:
求解方程: f(x) =x^3-x-1=0在区间 (1,1.5)内的根。
首先我们将方程写成这种形式:
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

用初始根x0=1.5带入右端,可以得到
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

这时,x0和x1的值相差比较大,所以我们要继续迭代求解,将x1再带入公式得
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

直到我们我们得到的解的序列收敛,即存在极值的时候,迭代结束。
下面是这个方程迭代的次数以及每次xi的解(i=0,1,2....)
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片


我们发现当k=7和8的时候,方程的解已经不再发生变化了,这时候我们就得到了此方程的近似解。
算法核心:

#define eps 1e-8 int main() { x0=初始近似根; do{ x1=x0; x0=g(x1); //按特定的方程计算新的近似根 }while(fabs(x0-x1)>Epsilon); printf("方程的近似根是%f\n",x0); }


注意:如果方程无解,算法求出的近似根序列就不会收敛,那么迭代过程就会变成死循环。因此,在使用迭代算法前应先考察方程是否有解,并在算法中对迭代次数给予限制。

下面再写一个求解方程组的例子加深一下理解:
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片


算法说明:
方程组解的初值X=(x0,x1,…,xn-1),迭代关系方程组为:xi=gi(X)(i=0,1,…,n-1),w为解的精度,maxn为迭代次数。
算法如下:
int main() { for (i=0; iw and k

选取初始向量 knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

精确度为1e-8,迭代次数为10.
求解代码如下:
#include #include #include #include #define eps 1e-8 using namespace std; const int maxn=100; double x[10],y[10]; int main() { for(int i=1; i<=4; i++) x[i]=0; int cnt=0; double c=0; do{ for(int i=1; i<=4; i++) y[i]=x[i]; for(int i=1; i<=4; i++) { x[1]=(6+x[2]-2*x[3])/10; x[2]=(25+x[1]+x[3]-3*x[4])/11; x[3]=(-11-2*x[1]+x[2]+x[4])/10; x[4]=(15-3*x[2]+x[3])/8; } c=0; for(int i=1; i<=4; i++) c+=(fabs(y[i]-x[i])); }while(c>eps&&cnt

运行结果如下:
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片


迭代法求解方程的过程是多样化的,比如牛顿迭代法,二分逼近法求解等。

二.牛顿迭代法
下面就是效率比较高且比较常用的牛顿迭代法:
牛顿迭代法又称为切线法,它比一般的迭代法有更高的收敛速度,如下图所示。
首先, 选择一个接近函数f(x)零点的x0, 计算相应的f(x0)和切线斜率f'(x0)(这里f '表示函数f的导数)。然后我们计算穿过点(x0,f (x0))且斜率为f '(x0)的直线方程
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

和x轴的交点的x的坐标,也就是求如下方程的解
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

将新求得交点的x坐标命名为x1。如图4所示,通常x1会比x0更接近方程f(x) = 0的解。接下来用x1开始下一轮迭代 .
迭代公式可化简为:
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

上式就是有名的牛顿迭代公式。已经证明, 如果f'是连续的, 并且待求的零点x是孤立的, 那么在零点x周围存在一个区域, 只要初始值x0位于这个邻近区域内, 那么牛顿法必定收敛。
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片


knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片

求形如ax^3+bx^2+cx+d=0方程根的算法,系数a、b、c、d的值依次为1、2、3、4,由主函数输入。求x在1附近的一个实根。求出根后由主函数输出。

由以上的公式可得到代码:
#include #include #include #include #define eps 1e-8 using namespace std; int main() { double a,b,c,d; cin>>a>>b>>c>>d; double x1=1,x,f,fx; do{ x=x1; f=((a*x+b)*x+c)*x+d; fx=(3*a*x+2*b)*x+c; x1=x-f/fx; }while(fabs(x1-x)>=eps); printf("%.2lf",x1); }

结果如下:
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片



三.二分逼近法
接下来说一下二分逼近法:
用二分法求解方程f(x)=0根的前提条件是:f(x)在求解的区间[a,b]上是连续的,且已知f(a)与f(b)异号,即 f(a)*f(b)<0。
令[a0,b0]=[a,b],c0=(a0+b0)/2,若f(c0)=0,则c0为方程f(x)=0的根;否则,若f(a0)与f(c0)异号,即 f(a0)*f(c0)<0,则令[a1,b1]=[a0,c0];若f(b0)与f(c0)异号,即 f(b0)*f(c0)<0,则令[a1,b1]=[c0,b0]。
依此做下去,当发现f(cn)=0时,或区间[an,bn]足够小,比如| an-bn |<0.0001时,就认为找到了方程的根。
knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板
文章图片


例:
用二分法求一元非线性方程f(x)= x^3/2+2x^2-8=0(其中^表示幂运算)在区间[0,2]上的近似实根r,精确到0.0001.
算法如下:
int main( ) { double x,x1=0,x2=2,f1,f2,f; print(“input a,b (f(a)*f(b)<0)”); input(a,b); f1=x1*x1*x1/2+2*x1*x1-8; f2=x2*x2*x2/2+2*x2*x2-8; if(f1*f2>0) { printf("No root"); return; } do{ x=(x1+x2)/2; f=x*x*x/2+2*x*x-8; if(f=0) break; if(f1*f>0.0) { x1=x; f1=x1*x1*x1/2+2*x1*x1-8; } else x2=x; } while(fabs(f)>=1e-4); print("root=",x); }



三.高斯消元入门详解及模板
此部分主要讲述解方程的方法,便于以后查找,转载于dalao博客:https://blog.csdn.net/pengwill97/article/details/77200372,先感谢大佬。

1、基本描述 学习一个算法/技能,首先要知道它是干什么的,那么高斯消元是干啥的呢?
高斯消元主要用来求解线性方程组,也可以求解矩阵的秩,矩阵的逆。在ACM中是一个有力的数学武器.
它的时间复杂度是n^3,主要与方程组的个数,未知数的个数有关。
那么什么是线性方程组呢?
简而言之就是有多个未知数,并且每个未知数的次数均为一次,这样多个未知数组成的方程组为线性方程组。
2、算法过程 其实高斯消元的过程就是手算解方程组的过程,回忆一下小的时候怎么求解方程组:加减消元,消去未知数,如果有多个未知数,就一直消去,直到得到类似kx=b(k和b为常数,x为未知数)的式子,就可以求解出未知数x,然后我们回代,依次求解出各个未知数的值,就解完了方程组。
换句话说,分两步:
1). 加减消元
2). 回代求未知数值
高斯消元就是这样的一个过程。
下面通过一个小例子来具体说明
0).求解方程组
有这样一个三元一次方程组:
?????2x+y+z=16x+2y+z=?1?2x+2y+z=7①②③{2x+y+z=1①6x+2y+z=?1②?2x+2y+z=7③

1).消去x
①×(?3)+②①×(?3)+②得到
0x?y?2z=?40x?y?2z=?4
①+③①+③得到
0x+3y+2z=80x+3y+2z=8
从而得到
?????2x+y+z=10x?y?2z=?40x+3y+2z=8①②③{2x+y+z=1①0x?y?2z=?4②0x+3y+2z=8③

2).消去y
②×3+③②×3+③得到
0x+0y?4z=?40x+0y?4z=?4
进而得到
?????2x+y+z=10x?y?2z=?40x+0y?4z=?4①②③{2x+y+z=1①0x?y?2z=?4②0x+0y?4z=?4③

至此,我们已经求解出来了
z=1z=1

下一步我们进行回代过程
3).回代求解y
将z=1z=1带入②②,求得
y=2y=2

进而得到
?????2x+y+z=1y=2z=1①②③{2x+y+z=1①y=2②z=1③

4).回代求解x
将z=1,y=2z=1,y=2带入①①,求得
x=?1x=?1

最终得到
?????x=?1y=2z=1①②③{x=?1①y=2②z=1③

至此,整个方程组就求解完毕了。

3、再解算法 对于方程组,其系数是具体存在矩阵(数组)里的,下面在给出实际在矩阵中的表示(很熟悉就可以跳过不看啦~)

0).求解方程组

??????x26?2y122z111val117??????[xyzval21116211?2217]

1).消去x

??????x200y1?13z1?22val1?48??????[xyzval21110?1?2?40328]

2).消去y

??????x200y1?10z1?2?4val1?4?4??????[xyzval21110?1?2?400?4?4]

3).回代求解y
回代的时候,记录各个变量的结果将保存在另外一个数组当中,故保存矩阵的数组值不会发生改变,该矩阵主要进行消元过程。
??????x200y1?10z1?2?4val1?4?4??????[xyzval21110?1?2?400?4?4]

4、再再解算法 说了这么多,其实有一些情况我们还没有说到。
通过上述的消元方法,其实我们比较希望得到的是一个上三角阵(省去了最后的val)
????2001?101?2?4????[2110?1?200?4]

下面问题来了:
Q1:系数不一定是整数啊?
A1:这时候数组就要用到浮点数了!不能是整数!
Q2:什么时候无解啊?
A2:消元完了,发现有一行系数都为0,但是常数项不为0,当然无解啦!比如:
??????x200y1?10z1?20val1?45??????[xyzval21110?1?2?40005]

Q3:什么时候多解啊?
A3:消元完了,发现有好几行系数为0,常数项也为0,这样就多解了!有几行为全为0,就有几个自由元,所谓自由元,就是这些变量的值可以随意取,有无数种情况可以满足给出的方程组,比如:
??????x200y100z100val100??????[xyzval211100000000]

您说这x,y,z不是无数组解嘛!

Q4:那什么时候解是唯一的啊!
A4:您做一下排除法,不满足2和3的,不就是解释唯一的嘛!其实也就是说我们的系数矩阵可以化成上三角阵。
【knowledge|解方程 ( 迭代法/牛顿迭代/高斯消元 ) 详解及模板】
5、代码实现 啰里啰嗦说了一堆,想必算法的流程已经熟悉了,那么高斯消元如何用代码实现呢?
戳这里有更多类型的 高斯消元模板。
(以下参考kuangbin大牛的模板)



#include #include #include #include #include using namespace std; const int MAXN=50; int a[MAXN][MAXN]; //增广矩阵 int x[MAXN]; //解集 bool free_x[MAXN]; //标记是否是不确定的变元 int gcd(int a,int b) { if(b == 0) return a; else return gcd(b,a%b); } inline int lcm(int a,int b) { return a/gcd(a,b)*b; //先除后乘防溢出 } // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解, //-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数) //有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var. int Gauss(int equ,int var) { int i,j,k; int max_r; // 当前这列绝对值最大的行. int col; //当前处理的列 int ta,tb; int LCM; int temp; for(int i=0; i<=var; i++) { x[i]=0; free_x[i]=true; }//转换为阶梯阵. col=0; // 当前处理的列 for(k = 0; k < equ && col < var; k++,col++) // 枚举当前处理的行. { // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) max_r=k; for(i=k+1; iabs(a[max_r][col])) max_r=i; } if(max_r!=k) // 与第k行交换. { for(j=k; j= 0; i--) { temp = a[i][var]; for (j = i + 1; j < var; j++) { if (a[i][j] != 0) temp -= a[i][j] * x[j]; } if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解. x[i] = temp / a[i][i]; } return 0; } int main(void) { int i, j,equ,var; while (scanf("%d %d", &equ, &var) != EOF) { memset(a, 0, sizeof(a)); for (i = 0; i < equ; i++) for (j = 0; j < var + 1; j++) scanf("%d", &a[i][j]); int free_num = Gauss(equ,var); if (free_num == -1) printf("无解!\n"); else if (free_num == -2) printf("有浮点数解,无整数解!\n"); else if (free_num > 0) { printf("无穷多解! 自由变元个数为%d\n", free_num); for (i = 0; i < var; i++) { if (free_x[i]) printf("x%d 是不确定的\n", i + 1); else printf("x%d: %d\n", i + 1, x[i]); } } else { for (i = 0; i < var; i++) printf("x%d: %d\n", i + 1, x[i]); } printf("\n"); } return 0; }




    推荐阅读