本文首發於微信公衆號:"算法與編程之美",歡迎關注,及時瞭解更多此係列文章。
問題描述
在日常生活中面對一元一次方程、一元二次方程甚至是一元三次方程時,我們都可以應用所學的數學知識比如因式分解和求根公式去將其輕易地解開。但並不是所有的方程都能進行因式分解或有求根公式又或者是求根公式複雜,這些問題都導致我們解方程時求解困難,形如x-cos(x)=0、e^x * sin(x)-cos(x)=0、x^4–2x^2+x=0等超越方程和高次方程我們人工計算就無法求得其解。
解決方案
我們可以應用牛頓法解決上述問題。牛頓法又稱切線法,簡單來說就是不斷求函數圖像的切線與x軸的交點,來逐漸接近函數解的一個迭代過程,其核心思想就是不斷地逼近函數的解。
牛頓法的步驟如下:
.選取一個初始迭代點,如‘1’;
.根據公式求出下一個接近零點的點;
③重複步驟②直到滿足:
1). (eps爲人設的精度要求)
2).達到最大迭代次數
牛頓法的原理如圖:
通過上圖我們可以發現,每迭代一次的值都將更接近方程的解。
接下來我們用Python代碼來實現牛頓法並求方程的解:
from sympy import * # 定義方程 def function(): x = symbols('x') # 符號變量的定義 fx = -exp(x) * sin(x) + cos(x) - 1 return fx
# 求解方程的一階導數 def diff_funtion(): fh = function() dth = fh.diff() return dth # 定義牛頓法 def Newton(x0,eps,maxiter): # x1 = x0 - f(x0)/f`(x0) ==> x2 = x1 - f(x1)/f`(x1) x = symbols('x') # 符號變量的定義 fh = function() #方程 dfh = diff_funtion() #方程一階導 x_n = x0 # x_next代表x(n+1)
print('%5s %14s %23s' %('迭代次數','計算結果','誤差')) #利用牛頓法逐漸逼近精確解 for k in range(maxiter): x_b = x_n # x_before代表x(n) fx = fh.evalf(subs = {x:x_b}) # 方程在xn處的數值 dfx = dfh.evalf(subs = {x:x_b}) # 方程的一階導在xn處的數值 x_n = x_b - fx/dfx errval = abs(fh.evalf(subs = {x:x_n})) #第k次迭代誤差大小
# 輸出迭代過程 print('%5d %25.15f %25.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('牛頓迭代法求解數值逼近,已達到最大迭代次數,可能不收斂或精度過高...')
fh = function() plot(fh) #畫出函數圖像
x0 = float(input('請輸入迭代初始值:')) eps = float(input('請輸入方程解的精度要求:')) maxiter = int(input('請輸入最大迭代次數:')) Newton(x0,eps,maxiter) |
運行結果如下:
用plot()畫出函數圖像方便選擇迭代初始點
結語
這裏簡單介紹了牛頓法,其本質是函數泰勒展開後,取一階來近似,即用線性函數近似f(x),畢竟線性函數更好算。應用牛頓法我們幾乎可以求解所有方程的近似解,而且精度極高。但通過上訴求方程-e^x*sin(x)+cos(x)-1=0的解我們可以看到牛頓法不能一次性求出所有的解,即當方程有多個解時,選擇不同的初始迭代點得到方程的解就不同。
作者:唐雷清
實習編輯:李欣容
稿件來源:深度學習與文旅應用實驗室(DLETA)
本文分享自微信公衆號 - 算法與編程之美(algo_coding)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。