1實驗內(nèi)容
用迭代法求下列方程的根
f(x)=x3+2x2+10x-20=0 (x*=1.368808107821372...),要求誤差< 10-15 。
(1)把方程改寫成等價的迭代格式:
x(k+1) =20/(xk^2+2xk+10),取初值x0=1, 求方程的根。
(2)把方程改寫成等價的迭代格式:
xk+1 = 2-(xk3+2xk2)/10,取初值x0=1, 求方程的根。
(3)對迭代格式(1)和(2)分別用Aitken法進(jìn)行加速,并對結(jié)果進(jìn)行分析。
2 算法原理
2.1 不動點迭代法
迭代法是一種逐次逼近的方法,用某種固定格式反復(fù)校正根的近似值,使之逐步精確,最后達(dá)到滿足精度要求的結(jié)果。
方程f(x)=0化為等價形式的方程x=φ(x) ,構(gòu)造迭代公式xk+1=φ(xk), k=0,1,2,……取初始近似根(迭代初值)x0,進(jìn)行迭代計算x1=φ(x0),x2=φ(x1),...,xk+1=φdf(xk),……
則有x1,x2 ,……,xk,……,得到迭代序列{xk}.如果這個序列有極限,則迭代公式是收斂的。
這時則x(x),x稱為函數(shù)φ(x)的不動點,等價地有f(x)=0,x*即為方程的根。連續(xù)函數(shù)φ(x)稱為迭代函數(shù)。
實際計算到|xk-xk-1|<ε(ε是預(yù)定的精度),取x*≈xk。
迭代公式收斂指迭代序列{xk}收斂,迭代公式發(fā)散指迭代序列{xk}不收斂,即發(fā)散。迭代公式不一定總是收斂。簡單迭代法的幾何意義如下:



圖2 收斂與發(fā)散的幾何對比
??=??(??) 的解稱為函數(shù) ??(??) 的不動點,幾何圖像為函數(shù)圖像與正比例函數(shù) ??=??的交點。迭代形式為:????+1=??(????) 。設(shè)函數(shù)的不動點為 ???,當(dāng)|??′(???)|<1 時,在不動點附近的存在一個領(lǐng)域,使得從領(lǐng)域內(nèi)的某個值開始的不動點迭代收斂,收斂到不動點。通過 ??????+1≈??′(???)??????式,當(dāng)導(dǎo)數(shù)絕對值小于1時,迭代后誤差的線性主部較迭代前誤差小,以導(dǎo)數(shù)絕對值為收斂常數(shù)線性收斂。唯一不同的是若導(dǎo)數(shù)值為零,此時該迭代至少二次收斂。用不動點迭代解非線性方程的核心在于將非線性方程轉(zhuǎn)化為一個收斂的不動點迭代。
2.2 Aitken加速算法
Aitken加速收斂算法基本原理:
對于收斂的迭代過程,只要迭代足夠多次,就可以使結(jié)果達(dá)到任意的精度。但有時迭代過程收斂緩慢,從而使計算量變得很大,因此,迭代過程的加速是個重要的過程。
2.3 牛頓迭代法原理
牛頓迭代法是用于求解等式方程的一種方法。類似于求解F(x)=0的根,牛頓迭代法求解的是近似根,這個想法準(zhǔn)確來說來源于泰勒展開式,需要求解的表達(dá)式可能非常復(fù)雜,通過一般的方法,我們很難求出它的解。
所以采用了一種近似求解的方法,取泰勒展開式的前幾項,隊原來的求解函數(shù)做一個取代,然后,求解這個取代原方程的方程的解,作為近似解。當(dāng)然只對原方程做一次近似求解不行,因為第一次近似肯定不會太準(zhǔn)確,所以還需要不斷地迭代。
首先就要取一個值作為初始的近似值,然后去求解該點的泰勒展開近似項,然后求解根,之后再以此根對原方程進(jìn)行近似,然后再求解結(jié)果不斷重復(fù)迭代,最終就能求得近似解。
牛頓迭代法迭代公式如下:
設(shè)x是f(x) = 0的根,選取x0作為x初始近似值,過點(x0, f(x))做曲線y = f(x)的切線L,則L的方程為y = f(x0)+f '(x0)(x-x0),求出L與x軸交點的橫坐標(biāo)x1= x0-f(x0) / f '(x0),稱x1為x的一次近似值。過點(x1, f(x1))做曲線y = f(x)的切線,并求該切線與x軸交點的橫坐標(biāo)x2= x1-f(x1)/ f '(x1),稱x2為x的二次近似值。重復(fù)以上過程,得r的近似值序列,其中xn+1=x(nf(xn)/f'(xn),稱為x的n+1次近似值,上式稱為牛頓迭代公式。
解非線性方程f(x)=0的牛頓法是把非線性方程線性化的一種近似方法。把f(x)在x0點附近展開成泰勒級數(shù)f(x) = f(x0)+(x-x0)f'(x0)+(x-x0)2*f''(x0)/2! +… 取其線性部分(一次),作為非線性方程f(x) = 0的近似方程,即泰勒展開的前兩項,則有f(x0)+f'(x0)(x-x0)=0 設(shè)f'(x0)≠0則其解為x1=x0-f(x0)/f'(x0) 這樣,得到牛頓法的一個迭代序列:x(n+1)= xn-f(xn) / f '(xn)。
牛頓迭代法,取得是泰勒展開式的前兩項,也就是線性近似,所以迭代比較快,容易計算。

3 程序框圖


4 編程實現(xiàn)
利用所編程序,求解下列方程的根:
以下將用Python進(jìn)行編程實驗,利用不動點迭代法與牛頓迭代法求解題目中方程的根,并且利用Aitken加速方法對其進(jìn)行加速,最后進(jìn)行結(jié)果分析與討論。
利用不動點迭代法的Python代碼詳解及運行結(jié)果:
(1) 把方程改寫成等價的迭代格式: xk+1+20/(xk2+2xk+10),取初值x0=1,求方程的根。
第(1)種迭代格式代碼詳解:
from sympy import *
def fun():
"""
求解方程的符號定義
:return:方程
error:這里不能用math庫中的函數(shù),pow()需要調(diào)用符號庫中的函數(shù),即sympy.pow()
或者用from sympy import * 來導(dǎo)入sympy中的所有函數(shù),之后直接寫pow()函數(shù)即
是默認(rèn)的sympy中的函數(shù)。
"""
x = symbols('x') # 符號變量的定義
# return 2 * exp(-x) * sin(x) + 2 * cos(x) - 0.25
return pow(x, 3) + 2.0 * pow(x, 2) + 10.0 * x - 20.0 # 返回函數(shù)的值
def fixedPoint(x0, eps, maxIter):
"""
不動點迭代法的作用是,求解非線性方程的根,采用逐步逼近的方法進(jìn)行計算
:param x0:迭代初值
:param eps:誤差精度要求
:param maxIter:最大迭代次數(shù)
:return:返回值為None
"""
x = symbols('x')
fh = fun()
x_n = x0 # 定義初值
k = 0 # 初始化迭代次數(shù)
errval = 0 # 初始化誤差
print('%3s %10s %18s' % ('迭代次數(shù)', '方程近似值', '迭代誤差'))
for k in range(maxIter):
x_b = x_n # 代表x_n
x_n = 20 / (pow(x_b, 2) + 2 * x_b + 10) # 寫出第一種迭代函數(shù)格式
errval = abs(fh.evalf(subs={x: x_n})) # 第k次迭代誤差的大小
print('%3d %22.15f %22.15f' % (k + 1, x_n, errval)) # 分別輸出迭代次數(shù),方程的近似根以及迭代誤差
if errval <= eps:
break
if k + 1 <= maxIter - 1:
print('方程在滿足精度' + str(eps) + '的條件下,近似解為:'
+ str(x_n) + ',誤差為:' + str(errval))
else:
print('不動點迭代法求解方程的根,已經(jīng)達(dá)到最大迭代次數(shù),也可能不收斂或精度過高...')
return None
if __name__ == '__main__':
fh = fun()
plot(fh)
x0 = float(input('請輸入迭代初值:')) # input函數(shù)總是以字符串的形式返回
eps = float(input('請輸入誤差精度要求:')) # 方程解的精度要求是近似解與真值之間的誤差
maxIter = int(input('請輸入最大迭代次數(shù):')) # 方程一般會迭代無數(shù)次,必須定義其迭代的次數(shù),以求收斂
fixedPoint(x0, eps, maxIter)
print('方程為:', '%30s' % (str(fun())))
print('迭代函數(shù)為:', '%20s' % ('20 / (x**2 + 2 * x + 10)'))




(2) 把方程改寫成等價的迭代格式: xk+1 = 2-(xk3+2xk2)/10,取初值x0=1, 求方程的根。
第二種迭代格式代碼詳解:
from sympy import *
def fun():
"""
求解方程的符號定義
:return:方程
error:這里不能用math庫中的函數(shù),pow()需要調(diào)用符號庫中的函數(shù),即sympy.pow()
或者用from sympy import * 來導(dǎo)入sympy中的所有函數(shù),之后直接寫pow()函數(shù)即
是默認(rèn)的sympy中的函數(shù)。
"""
x = symbols('x') # 符號變量的定義
# return 2 * exp(-x) * sin(x) + 2 * cos(x) - 0.25
return pow(x, 3) + 2.0 * pow(x, 2) + 10.0 * x - 20.0 # 返回函數(shù)的值
def fixedPoint(x0, eps, maxIter):
"""
不動點迭代法的作用是,求解非線性方程的根,采用逐步逼近的方法進(jìn)行計算
:param x0:迭代初值
:param eps:誤差精度要求
:param maxIter:最大迭代次數(shù)
:return:返回值為None
"""
x = symbols('x')
fh = fun()
x_n = x0 # 定義初值
k = 0 # 初始化迭代次數(shù)
errval = 0 # 初始化誤差
print('%3s %10s %18s' % ('迭代次數(shù)', '方程近似值', '迭代誤差'))
for k in range(maxIter):
x_b = x_n # 代表x_n
x_n = 2 - (pow(x_b, 3) + 2 * pow(x_b, 2))/10 # 寫出第二種迭代函數(shù)格式
errval = abs(fh.evalf(subs={x: x_n})) # 第k次迭代誤差的大小
print('%3d %22.15f %22.15f' % (k + 1, x_n, errval)) # 分別輸出迭代次數(shù),方程的近似根以及迭代誤差
if errval <= eps:
break
if k + 1 <= maxIter - 1:
print('方程在滿足精度' + str(eps) + '的條件下,近似解為:'
+ str(x_n) + ',誤差為:' + str(errval))
else:
print('不動點迭代法求解方程的根,已經(jīng)達(dá)到最大迭代次數(shù),也可能不收斂或精度過高...')
return None
if __name__ == '__main__':
fh = fun()
plot(fh)
x0 = float(input('請輸入迭代初值:')) # input函數(shù)總是以字符串的形式返回
eps = float(input('請輸入誤差精度要求:')) # 方程解的精度要求是近似解與真值之間的誤差
maxIter = int(input('請輸入最大迭代次數(shù):')) # 方程一般會迭代無數(shù)次,必須定義其迭代的次數(shù),以求收斂
fixedPoint(x0, eps, maxIter)
print('方程為:', '%30s' % (str(fun())))
print('迭代函數(shù)為:', '%20s' % ('2 - (x**3 + 2 * x**2)/10'))




(3) 對迭代格式(1)和(2)分別用Aitken法進(jìn)行加速,并對結(jié)果進(jìn)行分析。
迭代格式
第(1)種迭代格式Aitken加速法代碼詳解:
from sympy import *
def Aitken(x0, eps, iterNum, Phi):
xk_1 = x0
errval = 0
print('%3s %10s %18s' % ('迭代次數(shù)', '方程近似值', '迭代誤差'))
for k in range(iterNum):
y = Phi(xk_1)
z = Phi(y)
if (z - 2 * y + xk_1) != 0:
xk = xk_1 - (y - xk_1) ** 2 / (z - 2 * y + xk_1)
errval = abs(xk - xk_1)
print('%3d %22.15f %22.15f' % (k + 1, xk, errval))
if errval < eps:
return xk
else:
xk_1 = xk
else:
return xk
print('方法失敗')
return 0
def Phi(x):
return 20 / (pow(x, 2) + 2 * x + 10)
if __name__ == '__main__':
x = symbols('x')
x0 = float(input('請輸入迭代初值:')) # input函數(shù)總是以字符串形式返回
eps = float(input('請輸入迭代誤差精度要求:')) # 方程誤差精度要求為迭代近似值與真值之間的差值
iterNum = int(input('請輸入最大迭代次數(shù):')) # 最大迭代次數(shù)限制了方程的收斂
Phi(x)
Aitken(x0, eps, iterNum, Phi)

第(2)種迭代格式Aitken加速法代碼詳解:
from sympy import *
def Aitken(x0, eps, iterNum, Phi):
xk_1 = x0
errval = 0
print('%3s %10s %18s' % ('迭代次數(shù)', '方程近似值', '迭代誤差'))
for k in range(iterNum):
y = Phi(xk_1)
z = Phi(y)
if (z - 2 * y + xk_1) != 0:
xk = xk_1 - (y - xk_1) ** 2 / (z - 2 * y + xk_1)
errval = abs(xk - xk_1)
print('%3d %22.15f %22.15f' % (k + 1, xk, errval))
if errval < eps:
return xk
else:
xk_1 = xk
else:
return xk
print('方法失敗')
return 0
def Phi(x):
return 2 - (pow(x, 3) + 2 * pow(x, 2))/10
if __name__ == '__main__':
x = symbols('x')
x0 = float(input('請輸入迭代初值:')) # input函數(shù)總是以字符串形式返回
eps = float(input('請輸入迭代誤差精度要求:')) # 方程誤差精度要求為迭代近似值與真值之間的差值
iterNum = int(input('請輸入最大迭代次數(shù):')) # 最大迭代次數(shù)限制了方程的收斂
Phi(x)
Aitken(x0, eps, iterNum, Phi)

牛頓迭代法代碼詳解:
from sympy import *
def fun():
"""
求解方程的符號定義
:return:方程
error:這里不能用math庫中的函數(shù),pow()需要調(diào)用符號庫中的函數(shù),即sympy.pow()
或者用from sympy import * 來導(dǎo)入sympy中的所有函數(shù),之后直接寫pow()函數(shù)即
是默認(rèn)的sympy中的函數(shù)。
"""
x = symbols('x') # 符號變量的定義
# return 2 * exp(-x) * sin(x) + 2 * cos(x) - 0.25
return pow(x, 3) + 2.0 * pow(x, 2) + 10.0 * x - 20.0 # 返回函數(shù)的值
def diffFun():
"""
求解方程的一階導(dǎo)數(shù)
:return: 一階導(dǎo)數(shù)
"""
return fun().diff()
def newtonIterative(x0, eps, maxIter):
"""
牛頓迭代函數(shù)的作用,求解非線性方程的根,采用逼近法的思想
:param x0:迭代初值
:param eps:誤差精度要求
:param maxIter:最大迭代次數(shù)
:return:返回值為None
"""
# x1 = x0 - f(x0)/f'(x0)==>x1 = x2 - f(x1)/f'(x1)...
x = symbols('x')
fh = fun() # 引用方程
dfh = diffFun() # 引用方程的一階導(dǎo)數(shù)
x_n = x0
k = 0
errval = 0
print('%3s %10s %18s' % ('迭代次數(shù)', '方程近似值', '迭代誤差'))
# 利用牛頓迭代法的思想逐步逼近精確解
for k in range(maxIter):
x_b = x_n # 代表x(n)
fx = fh.evalf(subs={x: x_b}) # 方程在x_n處的數(shù)值
dfx = dfh.evalf(subs={x: x_b}) # 一階導(dǎo)數(shù)方程在x_n處的方程
x_n = x_b - fx / dfx # 牛頓迭代公式
errval = abs(fh.evalf(subs={x: x_n})) # 第k次迭代誤差的大小
print('%3d %22.15f %22.15f' % (k + 1, x_n, errval))
if errval <= eps:
break
if k + 1 <= maxIter - 1:
print('方程在滿足精度' + str(eps) + '的條件下,近似解為:'
+ str(x_n) + ',誤差為:' + str(errval))
else:
print('牛頓迭代法求解數(shù)值逼近,已經(jīng)達(dá)到最大迭代次數(shù),也可能不收斂或精度過高...')
# print(x0, eps, maxIter)
return None
if __name__ == '__main__':
fh = fun()
dfh = diffFun()
plot(fh)
plot(dfh)
x0 = float(input('請輸入迭代初值:')) # input函數(shù)總是以字符串的形式返回
eps = float(input('請輸入誤差精度要求:')) # 方程解的精度要求是近似解與真值之間的誤差
maxIter = int(input('請輸入最大迭代次數(shù):')) # 方程一般會迭代無數(shù)次,必須定義其迭代的次數(shù),以求收斂
newtonIterative(x0, eps, maxIter)
print('方程為:', '%30s' % (str(fun())))
print('方程的導(dǎo)數(shù)為:' '%22s' % (str(diffFun())))




5 結(jié)果分析與討論

由方程的曲線可以看出,此方程的根在1附近只有一個根,因此迭代初值取1可以降低迭代次數(shù),節(jié)省迭代時間。
(1)從不動點迭代法與牛頓迭代法的結(jié)果對比可以看出,在精度1e-15高精度的情況下,方程很難在100次迭代中得到近似解。但是牛頓迭代法對于方程的收斂速度高于不動點迭代法。
(2)在兩種不同的迭代格式下,從運行結(jié)果來看:第(1)種迭代格式收斂,但由于精度過高,達(dá)到最大迭代次數(shù);第(2)種迭代格式是發(fā)散的,不論精度,都不會收斂。
(3)分別對兩種迭代格式用Aitken法進(jìn)行加速,結(jié)果可以看出兩種迭代格式在1e-15的高精度下,均處于收斂的情況。不同的是第(1)種迭代格式在4此迭代后收斂,而第(2)種迭代格式卻需要5次迭代才能收斂。但通過Aitken法加速之后,出現(xiàn)了以下兩種情況:
1)由于精度過高引起的達(dá)到最大迭代次數(shù)的問題得以解決,函數(shù)收斂;
2)由于迭代格式的發(fā)散問題導(dǎo)致無法求解方程,在加速的情況下,也可以很快收斂,進(jìn)一步說明了Aitken加速法的巨大作用。
6 參考:
[1]https://baike.baidu.com/item/%E7%89%9B%E9%A1%BF%E8%BF%AD%E4%BB%A3%E6%B3%95/10887580?fr=aladdin
[2]https://blog.csdn.net/weixin_45720246/article/details/115916645
[3]https://www.freesion.com/article/6284628111/
[5]https://www.bilibili.com/video/BV1Lq4y1U7Hj?spm_id_from=333.999.0.0
[6]《數(shù)值分析》歐陽潔等。