演算法分析與設計-迭代法求解方程(組)的根(詳解)
演算法分析設計課之期末考試前的重要演算法複習總結。。。
以下內容大多都摘抄自上課的課件的內容,但是課件沒有解方程的完整程式碼,於是自己又寫了寫程式碼,僅供參考。
首先,迭代法解方程的實質是按照下列步驟構造一個序列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)連續,則很容易得到這個極限值,x*就是方程f(x)=0的根。
舉個例子:
求解方程: f(x) =x^3-x-1=0 在區間 (1,1.5)內的根。
首先我們將方程寫成這種形式:
用初始根x0=1.5帶入右端,可以得到
這時,x0和x1的值相差比較大,所以我們要繼續迭代求解,將x1再帶入公式得
直到我們我們得到的解的序列收斂,即存在極值的時候,迭代結束。
下面是這個方程迭代的次數以及每次xi的解(i=0,1,2....)
我們發現當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); }
注意:如果方程無解,演算法求出的近似根序列就不會收斂,那麼迭代過程就會變成死迴圈。因此,在使用迭代演算法前應先考察方程是否有解,並在演算法中對迭代次數給予限制。
下面再寫一個求解方程組的例子加深一下理解:
演算法說明:
方程組解的初值X=(x0,x1,…,xn-1),迭代關係方程組為:xi=gi(X)(i=0,1,…,n-1),w為解的精度,maxn為迭代次數。
演算法如下:
int main() { for (i=0;i<n;i++) x[i]=初始近似根 do{ k=k+1; for(i=0;i<n;i++) y[i]=x[i]; for(i=0;i<n;i++) x[i]=gi(X); //按特定的方程計算新的近似根 for(i=0;i<n;i++) c=c+fabs(y[i]-x[i]);//c要每次重新設初值為0 }while(c>w and k<maxn ); for(i=0;i<n;i++) print("變數的近似根是",x[i]); }
選取初始向量
精確度為1e-8,迭代次數為10.
求解程式碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#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<maxn);
for(int i=1;i<=4;i++)
printf("x%d = %.4lf\n",i,x[i]);
}
執行結果如下:
迭代法求解方程的過程是多樣化的,比如二分逼近法求解,牛頓迭代法等。
下面就是效率比較高且比較常用的牛頓迭代法:
牛頓迭代法又稱為切線法,它比一般的迭代法有更高的收斂速度,如下圖所示。
首先, 選擇一個接近函式f(x)零點的x0, 計算相應的f(x0)和切線斜率f'(x0)(這裡f ' 表示函式f的導數)。然後我們計算穿過點 (x0,f (x0))且斜率為f '(x0)的直線方程
和x軸的交點的x的座標,也就是求如下方程的解
將新求得交點的x座標命名為x1。如圖4所示,通常x1會比x0更接近方程f(x) = 0的解。接下來用x1開始下一輪迭代 .
迭代公式可化簡為:
上式就是有名的牛頓迭代公式。已經證明, 如果f' 是連續的, 並且待求的零點x是孤立的, 那麼在零點x周圍存在一個區域, 只要初始值x0位於這個鄰近區域內, 那麼牛頓法必定收斂。
求形如ax^3+bx^2+cx+d=0方程根的演算法,係數a、b、c、d的值依次為1、2、3、4,由主函式輸入。求x在1附近的一個實根。求出根後由主函式輸出。
由以上的公式可得到程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#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);
}
結果如下:
接下來說一下二分逼近法
用二分法求解方程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時,就認為找到了方程的根。
例:
用二分法求一元非線性方程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);
}
完結撒花~