维基百科有一个quick overview of secant method,看起来像
将第一次迭代翻译成 Python 看起来像
x2 = (x0 * f(x1) - x1 * f(x0)) / (f(x1) - f(x0))
与其多次调用f(x0)和f(x1),不如调用一次并存储值:
fx0 = f(x0)
fx1 = f(x1)
x2 = (x0 * fx1 - x1 * fx0) / (fx1 - fx0)
对于第二次迭代,我们必须计算
x3 = (x1 * fx2 - x2 * fx1) / (fx2 - fx1)
但我们不想每次都用新变量重新编写方程。相反,我们将值移动:让x1 取x2 的值,x2 取x3 的值,等等。我们可以像这样在循环中重复执行此操作
# store initial values
fx0 = f(x0)
fx1 = f(x1)
while (something):
# do calculation
x2 = (x0 * fx1 - x1 * fx0) / (fx1 - fx0)
# shift variables (prepare for next loop)
x0, x1 = x1, x2
fx0, fx1 = fx1, f(x2)
现在我们只需要知道何时停止,当我们最后一个答案与 0 之间的差值小于tol:abs(f(x2) - 0) < tol,可以简化为abs(fx1) < tol。
所以函数看起来像
def secant(f, x0, x1, tol):
# store initial values
fx0 = f(x0)
fx1 = f(x1)
while abs(fx1) < tol:
# do calculation
x2 = (x0 * fx1 - x1 * fx0) / (fx1 - fx0)
# shift variables (prepare for next loop)
x0, x1 = x1, x2
fx0, fx1 = fx1, f(x2)
return x1
现在我们只剩下一个问题:如果循环永远不会结束会发生什么?割线法不能保证收敛到一个解。如果函数运行次数过多,我们需要一种方法来停止它。
如果我们将while 循环更改为for 循环,则可以保证在设定的次数后停止。如果在此之前找到答案,我们可以提前退出;否则我们会引发一个错误,让用户知道它没有收敛。最终的函数看起来像
def secant(f, x0, x1, tol, max_iterations=100):
# keep initial values for error reporting
init_x0 = x0
init_x1 = x1
# store y values instead of recomputing them
fx0 = f(x0)
fx1 = f(x1)
# iterate up to maximum number of times
for _ in range(max_iterations):
# see whether the answer has converged
if abs(fx1) < tol:
return x1
# do calculation
x2 = (x0 * fx1 - x1 * fx0) / (fx1 - fx0)
# shift variables (prepare for next loop)
x0, x1 = x1, x2
fx0, fx1 = fx1, f(x2)
# for loop has ended - failed to converge
raise ValueError(
"call to secant(f={}, x0={}, x1={}, tol={}," \
" max_iterations={}) has failed to converge"
.format(
f.__name__,
init_x0,
init_x1,
tol,
max_iterations
)
)