迭代法的作用
许多复杂的求解问题,都可以转换成方程f(x)=0的求解问题。这一系列的解叫做方程的根。对于非线性方程的求解,在自变量范围内往往有多个解,我们将此变化区域分为多个小的子区间,对每个区间进行分别求解。我们在求解过程中,选取一个近似值或者近似区间,然后运用迭代方法逐步逼近真实解。
方程求根的常用迭代法有:二分法、不动点迭代、牛顿法、弦截法。
弦截迭代法
弦截法又称弦位法、弦割法、弦法或割线法
牛顿迭代法的优点是收敛速度快,但其明显的缺点是每次迭代需要计算导数值f’(xk),如果函数f(x)比较复杂,使用牛顿迭代法是不方便的。为了避开导数的计算,改用差商替换牛顿迭代法中的导数,这就是弦截法的基本思想。
单点弦法
这个公式的几何意义是过两个点作弦,这个弦与x轴的交点即是根的新的近似值,因为弦的一个端点(x0,f(x0))始终不变,另一个端点变动,所以这种方法称为单点弦法。
单点弦法例题——有区间
求方程式:x3- 0.165x2 + 3.993×10-4 = 0在(0,0.11)的根
先看看不用迭代法计算的结果
from sympy import *
from sympy.abc import x
def func(x):
return x**3 - 0.165*x**2 + 3.993*10**(-4)
result = solveset(func(x), x, Interval(0, 0.11))
print(result)
结果:
FiniteSet(0.0623775815137495)
约定一个误差,当误差小于某个数值的时候,迭代停止
代码:
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
if error < 10**(-6):
print(f'迭代第{i}次后,误差小于10^-6')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
结果:
迭代第14次后,误差小于10^-6
所求方程式的根为0.06237756428811298
迭代至电脑默认误差为0
代码:
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
if error == 0:
print(f'迭代第{i}次后,误差为0')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
结果:
迭代第39次后,误差为0
所求方程式的根为0.06237758151374951
画迭代图
代码:
import matplotlib.pyplot as plt
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
x_values = []
y_values = []
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
x_values.append(i)
y_values.append(error)
if error == 0:
print(f'迭代第{i}次后,误差为0')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
#坐标轴负号的处理
plt.rcParams['axes.unicode_minus']=False
#横坐标是迭代次数
#纵坐标是误差值
plt.plot(x_values,
y_values,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 3, # 点的大小
)
# 修改x轴和y轴标签
plt.xlabel('迭代次数')
plt.ylabel('误差值')
# 显示图形
plt.show()
结果:
迭代第39次后,误差为0
所求方程式的根为0.06237758151374951
单点弦法例题——单点
求方程式:x = exp(-x)在0.5附近的根
即求方程式xexp(x)-1=0在0.5附近的根
单点弦法是需要两个初始点,只是初始点x0在迭代过程中不变。所以另一个初始点可以取x0=0.5附近的值,这里我取x1=0.6。
代码:
from sympy import *
import matplotlib.pyplot as plt
x0 = 0.5
x1 = 0.6
x_list = [x1]
i = 0
x_values = []
y_values = []
def f(x):
f = x * exp(x) - 1
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
x_values.append(i)
y_values.append(error)
if error == 0:
print(f'迭代第{i}次后,误差为0')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
#坐标轴负号的处理
plt.rcParams['axes.unicode_minus']=False
#横坐标是迭代次数
#纵坐标是误差值
plt.plot(x_values,
y_values,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 3, # 点的大小
)
# 修改x轴和y轴标签
plt.xlabel('迭代次数')
plt.ylabel('误差值')
# 显示图形
plt.show()
结果:
迭代第14次后,误差为0
所求方程式的根为0.567143290409784
双点弦法
为了加速收敛,改用两个端点都在变动的弦,即用商差(f(xk)-f(xk-1))/(xk-xk-1)替代牛顿公式中的导数f’(x),而导出下图的弦截公式。这种迭代法称为双点弦法、快速弦法或离散牛顿法。
双点弦法的收敛速度比单点弦法快,仅稍慢于牛顿法,是超线性收敛的。
双点弦法在计算xk+1时要用到前面两点的值xk,xk+1,这种迭代法称为两步法,使用这类方法,在计算前必须先提供两个初始值。
双点弦法的例题——有区间
求方程式:x3- 0.165x2 + 3.993×10-4 = 0在(0,0.11)的根
约定一个误差,当误差小于某个数值的时候,迭代停止
代码:
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x0 = x1
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
if error < 10**(-6):
print(f'迭代第{i}次后,误差小于10^-6')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
结果:
迭代第6次后,误差小于10^-6
所求方程式的根为0.06237758151374951
迭代至电脑默认误差为0
代码:
from sympy import *
x = symbols('x')
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x0 = x1
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
if error == 0:
print(f'迭代第{i}次后,误差为0')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
结果:
迭代第7次后,误差为0
所求方程式的根为0.06237758151374951
画迭代图
代码:
import matplotlib.pyplot as plt
x0 = 0 #区间下限
x1 = 0.11 #区间上限
x_list = [x1]
i = 0
x_values = []
y_values = []
def f(x):
f = x**3 - 0.165*x**2 + 3.993*10**(-4)
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x0 = x1
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
x_values.append(i)
y_values.append(error)
if error < 10**(-6):
print(f'迭代第{i}次后,误差小于10^-6')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
#坐标轴负号的处理
plt.rcParams['axes.unicode_minus']=False
#横坐标是迭代次数
#纵坐标是误差值
plt.plot(x_values,
y_values,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 3, # 点的大小
)
# 修改x轴和y轴标签
plt.xlabel('迭代次数')
plt.ylabel('误差值')
# 显示图形
plt.show()
结果:
迭代第6次后,误差小于10^-6
所求方程式的根为0.06237758151374951
可与牛顿迭代法进行比较:
牛顿迭代法(Newton’s Method)迭代求根的Python程序
双点弦法例题——单点
求方程式:x = exp(-x)在0.5附近的根
即求方程式xexp(x)-1=0在0.5附近的根
双点弦法需要两个初始点,所以另一个初始点可以取x0=0.5附近的值,这里我取x1=0.6。
代码:
from sympy import *
import matplotlib.pyplot as plt
x0 = 0.5
x1 = 0.6
x_list = [x1]
i = 0
x_values = []
y_values = []
def f(x):
f = x * exp(x) - 1
return f
while True:
x2 = x1 - f(x1)*(x1-x0)/(f(x1)-f(x0))
x0 = x1
x1 = x2
x_list.append(x2)
if len(x_list) > 1:
i += 1
error = abs((x_list[-1] - x_list[-2]) / x_list[-1])
x_values.append(i)
y_values.append(error)
if error == 0 :
print(f'迭代第{i}次后,误差为0')
break
else:
pass
print(f'所求方程式的根为{x_list[-1]}')
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
#坐标轴负号的处理
plt.rcParams['axes.unicode_minus']=False
#横坐标是迭代次数
#纵坐标是误差值
plt.plot(x_values,
y_values,
color = 'steelblue', # 折线颜色
marker = 'o', # 折线图中添加圆点
markersize = 3, # 点的大小
)
# 修改x轴和y轴标签
plt.xlabel('迭代次数')
plt.ylabel('误差值')
# 显示图形
plt.show()
结果:
迭代第6次后,误差为0
所求方程式的根为0.567143290409784