การหาผลเฉลยของสมการเชิงอนุพันธ์สามัญด้วย Python#
บทความนี้กล่าวถึง การเขียนโค้ด Python โดยใช้โมดูล เช่น SymPy และ NumPy / SciPy เพื่อหาผลเฉลยของสมการเชิงอนุพันธ์สามัญ
Keywords: Python, Ordinary Differential Equations (ODE), ODE Solving, Symbolic Methods, Numerical Methods
▷ สมการเชิงอนุพันธ์สามัญ#
สมการเชิงอนุพันธ์ (Differential Equations: DE) เป็นสมการที่ประกอบไปด้วยฟังก์ชัน เช่น ซึ่งเป็นตัวแปรตาม (Dependent Variable) ที่ขึ้นอยู่กับ ซึ่งเป็นตัวแปรอิสระ หรือ ตัวแปรต้น (Independent Variable) รวมถึงอนุพันธ์อันดับต่าง ๆ ของฟังก์ชัน เช่น , , และอันดับของสมการเชิงอนุพันธ์คือ อันดับสูงสุดของอนุพันธ์ของฟังก์ชันที่มีอยู่ในสมการ
หากฟังก์ชันขึ้นอยู่กับตัวแปรอิสระเพียงตัวเดียว สมการเชิงอนุพันธ์นี้จะเรียกว่า สมการเชิงอนุพันธ์สามัญ (Ordinary Differential Equation: ODE) แต่ถ้ามีตัวแปรต้นหลายตัวและมีการใช้อนุพันธ์เทียบกับตัวแปรเหล่านั้นหลายตัวจะเรียกว่า สมการเชิงอนุพันธ์ย่อย (Partial Differential Equation: PDE)
ในทางวิศวกรรมศาสตร์ เช่น การศึกษาและวิเคราะห์วงจรไฟฟ้าแบบเชิงเส้น (Linear Circuit Analysis) ก็เกี่ยวข้องกับสมการเชิงอนุพันธ์ซึ่งมีตัวแปรต้นเป็นเวลา และการหาผลเฉลยหรือรูปแบบของฟังก์ชันที่ทำให้สมการเชิงอนุพันธ์เป็นจริงนั้น ทำได้หลายวิธีในทางคณิตศาสตร์ เช่น การใช้วิธีการแปลงลาปลาส (Laplace Transforms) สำหรับสมการเชิงอนุพันธ์สามัญเชิงเส้นที่มีสัมประสิทธิ์คงตัว เป็นต้น
แต่ในบทความนี้จะกล่าวถึง การใช้ซอฟต์แวร์และคำสั่งต่าง ๆ ของ Python (ใช้เวอร์ชัน Python 3.8) โดยแบ่งเป็นสองแนวทาง คือ
- การประมวลผลเชิงสัญลักษณ์ (Symbolic Methods) โดยใช้คำสั่งของโมดูล SymPy
- การประมวลผลเชิงตัวเลข (Numerical Methods) เช่น การใช้คำสั่งของโมดูล NumPy และ SciPy
ในส่วนของการแสดงผล มีการใช้โมดูล Matplotlib เพื่อแสดงรูปกราฟของฟังก์ชัน และ สำหรับการเขียนและรันโค้ดเพื่อดูผลลัพธ์ ก็สามารถใช้ Python IDE อย่างเช่น Spyder 3 หรือจะใช้ Jupyterlab ก็ได้เช่นกัน
ดังนั้นถ้าจะทดลองรันโค้ด จะต้องมีการติดตั้ง Python 3 Interpreter ในคอมพิวเตอร์ของผู้ใช้ก่อน
(Python Download & Install)
และติดตั้งโมดูล เช่น matplotlib
, numpy
, scipy
, ipython
, jupyterlab
โดยใช้คำสั่ง pip
▷ ตัวอย่างที่ 1#
ตัวอย่างแรกเป็นโจทย์สมการเชิงอนุพันธ์อันดับที่หนึ่ง
หากคำนวณด้วยวิธีทางคณิตศาสตร์ ก็มีวิธีการดังนี้ โดยใช้เทคนิคที่เรียกว่า วิธีแยกตัวแปร (Separation of Variables) แล้วอินทิเกรตทั้งสองตัวแปรที่เกี่ยวข้อง
โดยที่ และ เป็นค่าคงที่ใด ๆ และไม่เป็นศูนย์ และค่าของ จะหาได้ ถ้ามีการกำหนดเงื่อนไขค่าเริ่มต้น (Initial Condition) เช่น ให้ ดังนั้น
ตัวอย่างการเขียนโค้ด Python เพื่อหาผลเฉลย ของ ODE
โดยวิธีการประมวลผลเชิงสัญลักษณ์ทางคณิตศาสตร์
เริ่มต้นด้วยการสร้างตัวแปรที่เป็นสัญลักษณ์ทางคณิตศาสตร์โดยใช้คำสั่ง symbols()
ของ SymPy ได้แก่
t
ซึ่งเป็นตัวแปรอิสระ และฟังก์ชันทางคณิตศาสตร์ Function()
สำหรับ
การหาอนุพันธ์ของฟังก์ชัน ทำได้โดยใช้คำสั่ง diff()
หรือ คำสั่ง Derivative()
กับตัวแปรที่เป็นสัญลักษณ์ y(t)
และนำไปใช้ในการสร้างสมการเชิงอนุพันธ์ด้วยคำสั่ง Eq()
ซึ่งจะต้องระบุอาร์กิวเมนต์สองตัว คือ สิ่งที่อยู่ด้านซ้ายและขวามือ
ของเครื่องหมายเท่ากับในสมการเชิงอนุพันธ์
คำสั่ง dsolve()
จะถูกเรียกใช้ เพื่อหาผลเฉลยซึ่งเป็นคำตอบสำหรับฟังก์ชัน และสามารถแสดงเป็นข้อความเอาต์พุต
ได้โดยใช้คำสั่ง print()
หรือจะใช้ display()
ของ IPython เพื่อแสดงผลลัพธ์ในทางคณิตศาสตร์ให้ดูง่ายขึ้น
ฟังก์ชันเชิงสัญลักษณ์ที่เป็นผลเฉลย จะถูกแปลงให้เป็นฟังก์ชันที่นำไปใช้กับอาร์เรย์ของ numpy
เพื่อคำนวณค่าได้ โดยใช้คำสั่ง
lambdify()
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display
# define a symbol: use t as an independent variable
t, k = symbols('t k')
# define a function y(t)
y = Function('y')(t)
# compute the first derivative of y(t)
dydt = y.diff(t)
# define the ODE
ode = Eq( dydt, -k*t*y )
# substitute k with 2
ode = ode.subs( k, 2 )
# set the initial condtion: y(0) = 1.0
ics = { y.subs(t,0): 1.0 }
# solve the ODE with an initial condition (the IVP)
sol = dsolve( ode, ics=ics )
print( sol.lhs, '=', sol.rhs )
display( Eq( sol.lhs, sol.rhs ) )
# convert the the solution into a callable function
f = lambdify( t, sol.rhs )
# create an array of 100 time steps between [0,1]
ts = np.linspace(0,1,100)
plt.clf()
plt.plot( ts, f(ts) )
plt.grid()
plt.xlabel('t')
plt.ylabel('y(t)')
plt.show()
รูป: ตัวอย่างการเขียนโค้ด Python และรันโค้ดด้วย Spyder 3 IDE
รูป: กราฟแสดงผลเฉลยสำหรับฟังก์ชัน ในช่วง
ในเชิงเปรียบเทียบ การเขียนโค้ด Python โดยวิธีการประมวลผลเชิงตัวเลข จะต้องมีการกำหนดช่วงสำหรับตัวแปร เช่น กำหนดให้ ( และ ) เป็นต้น และจะต้องกำหนดค่าตัวเลขสำหรับเงื่อนไขเริ่มต้นด้วย ซึ่งจะถูกใช้เป็นจุดเริ่มต้นในการคำนวณของค่าของฟังก์ชันในตำแหน่งของ ถัดไป
การหาผลเฉลยของสมการเชิงอนุพันธ์ด้วย Python เราสามารถใช้คำสั่ง solve_ivp()
ของ scipy.integrate
ได้ดังนี้
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
t0 = 0.0 # start point for t
tf = 1.0 # end point for t
h = 0.05 # step size
y0 = 1.0 # initial value: y(t0) = y0
k = 2
# the exact solution found by analytic method
f_exact = lambda t,y0: y0*np.exp(-k/2*ts**2)
# y' = f(t,y) = -k*t*y
f = lambda t,y: -k*t*y
ts = np.arange(t0,tf,h) # array of time steps
sol = solve_ivp( f, [t0,tf], [y0], t_eval=ts, method='RK45' )
plt.clf()
plt.plot( ts, sol.y[0], 'o', ts, f_exact(ts,y0), '-' )
plt.grid()
plt.xlabel( 't' )
plt.ylabel( 'y(t)' )
plt.legend( ['numeric','analytic'] )
plt.show()
การใช้คำสั่ง solve_ivp()
จะต้องมีอาร์กิวเมนต์ เช่น
ฟังก์ชัน f
สำหรับการคำนวณค่าของ การกำหนดค่าสำหรับตัวแปร ts
(เป็นอาร์เรย์ของ Time Steps) ในช่วง [t0,tf]
ซึ่งมีระยะห่างคงที่ h
เท่ากับ 0.05
และจะต้องกำหนดค่าเริ่มต้น เช่น [y0]
สำหรับ ให้มีค่าเท่ากับ 1.0
ตามลำดับ
และมีการเลือกวิธีการในการหาผลเฉลย method
ให้เป็น RK45
ซึ่งหมายถึง
Runge-Kutta (4-th order)
ผลจากการใช้คำสั่ง solve_ivp()
สามารถนำไปใช้ในการแสดงรูปกราฟจากชุดข้อมูล
รูป: แสดงรูปกราฟเปรียบระหว่างผลเฉลยที่ได้จากสองวิธีการ
การคำนวณด้วยวิธีการของออยเลอร์ (Euler Method)
วิธีการคำนวณเชิงตัวเลข เพื่อหาค่าประมาณของผลเฉลยของสมการเชิงอนุพันธ์อันดับหนึ่ง มีหลายวิธี แต่วิธีการหนึ่งที่เป็นพื้นฐาน เรียกว่า Euler Method แบ่งเป็นสองกรณีคือ
- Forward (Explicit) Euler Method
- Backward (Implicit) Euler Method
อนุพันธ์อันดับหนึ่งของฟังก์ชัน ก็คือ หรือ เขียนค่าประมาณได้ดังนี้
จากสมการเชิงอนุพันธ์อันดับหนึ่ง ก็สามารถเขียนสูตรการคำนวณได้ดังนี้
ถ้าให้ เป็นระยะห่างและมีค่าคงที่ (เรียกว่า step-size) การคำนวณจะเริ่มจากจุดแรก คือ ไปยังตำแหน่งถัดไป คือ ซึ่งหมายถึง ในทิศทางไปข้างหน้า (ค่าของ เพิ่มขึ้น และ ) และเว้นระยะห่างเท่ากับ
ดังนั้นการคำนวณด้วยวิธี Forward Euler Method จะเป็นดังนี้
โดยเริ่มต้นที่จุด และคำนวณค่าถัดไปของฟังก์ชันในตำแหน่งถัดไป สำหรับ ตามลำดับ ซึ่งมีระยะห่างเท่ากับ
จากโจทย์ในตัวอย่างที่ 1 ก็สามารถเขียนสูตรการคำนวณตามวิธีของออยเลอร์แบบมองไปข้างหน้าได้ดังนี้
แต่ถ้าเป็นวิธี Backward Euler Method จะเป็นดังนี้
ซึ่งจะต้องมีจัดรูปใหม่หรือการแก้สมการ เพื่อหาค่า ในการคำนวณ และสำหรับโจทย์ตัวอย่างที่ 1 จะเขียนสูตรการคำนวณได้ดังนี้
และถ้านำไปจัดรูปใหม่ของสมการ จะได้เป็น
ลองมาดูตัวอย่างการเขียนโค้ด Python สำหรับสองวิธีของออยเลอร์ และเปรียบเทียบผลลัพธ์ที่ได้
import numpy as np
import matplotlib.pyplot as plt
# y'(t) = -2*t*y(t) = f(t,y)
t0 = 0.0 # start point for t
tf = 1.0 # end point for t
h = 0.05 # step size
y0 = 1.0 # initial value: y(t0) = y0
f = lambda t,y: -2*t*y
f_exact = lambda t: np.exp(-t**2)
ts = np.arange(t0,tf,h)
# forward Euler
y = np.zeros( len(ts) )
y[0] = y0
for i in range(len(ts)-1):
y[i+1] = y[i] + h*f(ts[i],y[i])
y_f = y
# backward Euler
y = np.zeros( len(ts) )
y[0] = y0
for i in range(len(ts)-1):
y[i+1] = y[i] / (1+2*h*ts[i+1])
y_b = y
plt.clf()
plt.plot( ts, y_f, 'o', ts, y_b, 'o', ts, f_exact(ts), '-')
plt.grid()
plt.xlabel( 't' )
plt.ylabel( 'y(t)' )
plt.legend(['forward','backward','analytic'])
plt.show()
รูป: แสดงรูปกราฟเปรียบระหว่างผลเฉลยที่ได้จากสองวิธีการของออยเลอร์ (ใช้ค่า )
▷ ตัวอย่างที่ 2#
ตัวอย่างที่สองเป็นสมการเชิงอนุพันธ์อันดับที่สองที่มีการกำหนดค่าเริ่มต้นสองเงื่อนไข
หากใช้วิธีการในทางคณิตศาสตร์สำหรับสมการเชิงอนุพันธ์สามัญเชิงเส้นที่มีสัมประสิทธิ์คงตัว ให้เริ่มต้นด้วยการกำหนดให้ทางขวาของสมการเป็นศูนย์ ซึ่งจะได้สมการที่เป็นเอกพันธ์ (Homogeneous) และหาผลเฉลยของสมการ จากนั้นจึงพิจารณาสมการที่ไม่เป็นเอกพันธ์ (Homogeneous) เพื่อหาผลเฉลยเฉพาะ ตามลำดับ
จากสมการที่เป็นเอกพันธ์ สามารถเขียนสมการลักษณะเฉพาะ (Characteristic Equation) ที่เกี่ยวข้อง ซึ่งเป็นพหุนามโดยใช้ตัวแปร ได้ดังนี้
โดยปรกติแล้ว ถ้าหาราก (roots) ของสมการลักษณะเฉพาะได้เป็น และไม่เป็นค่าที่ซ้ำกัน ก็สามารถเขียนรูปแบบของฟังก์ชันที่เป็นผลเฉลยได้ดังนี้
แต่เนื่องจากในตัวอย่างนี้ ได้รากของสมการที่ซ้ำกัน
ดังนั้นจะต้องเขียนรูปแบบของฟังก์ชัน ที่เป็นผลเฉลยดังนี้
ถัดไปเป็นการพิจารณาสมการที่ไม่เป็นเอกพันธ์ และทางขวามือของสมการเป็นฟังก์ชัน ดังนั้นรูปแบบของฟังก์ชันสำหรับผลเฉลยเฉพาะ เป็นดังนี้
โดยที่ และ เป็นค่าคงที่ใด ๆ ดังนั้นจะต้องหาค่า และ ที่ทำให้สมการเชิงอนุพันธ์เป็นจริง หากใช้ เป็นผลเฉลยในสมการดังกล่าว
จากนั้นให้นำฟังก์ชัน และอนุพันธ์ของฟังก์ชัน และ ไปแทนลงในสมการเชิงอนุพันธ์ แล้วจัดกลุ่มแยกตามพจน์สำหรับ กับ ดังนี้
เมื่อแก้ระบบสมการสองตัวแปรจะได้ และ
ผลเฉลยทั่วไปที่ได้ เป็นผลรวมของ และ ในรูปแบบของฟังก์ชันต่อไปนี้
จากเงื่อนไขค่าเริ่มต้น และ จะได้ระบบสมการที่สามารถนำไปหาค่า ของ และ ได้
โดยสรุป จะได้ฟังก์ชัน ที่เป็นผลเฉลยของปัญหาค่าเริ่มต้นดังนี้
จะเห็นได้ว่า การหาผลเฉลยของสมการเชิงอนุพันธ์ในตัวอย่างนี้มีหลายขั้นตอน
ถัดไปลองมาดูตัวอย่างเขียนโค้ด Python ด้วยวิธีการคำนวณเชิงสัญลักษณ์ เพื่อใช้ในการหาคำตอบ
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, Latex
# define a symbol: use t as an independent variable
t = symbols('t')
# define a function y(t)
y = Function('y')(t)
# compute the first derivate of y(t)
D1y = y.diff(t)
D2y = y.diff(t,2)
# 2nd-order ODE: y'' + 2y' + y = sin(2t)
# Initial conditions: y(0)=-1, y'(0)=1
# define the ODE
ode = Eq( D2y + 2*D1y + y, sin(2*t) )
# set the initial condition: y(0) = 1.0
ics = { y.subs(t,0): -1, D1y.subs(t,0): 1 }
# solve the ODE with an initial condition (the IVP)
sol = dsolve( ode, ics=ics )
print( sol.lhs, '=', sol.rhs )
display( Eq( sol.lhs, sol.rhs ) )
# => y(t) = (2*t/5 - 21/25)*exp(-t) - 3*sin(2*t)/25 - 4*cos(2*t)/25
# convert the the solution into a callable function
f = lambdify( t, sol.rhs )
# create an array of 100 time steps between [t0,tf]
t0 = 0.0
tf = 10.0
ts = np.linspace(t0,tf,100)
plt.clf()
plt.plot( ts, f(ts) )
plt.grid()
plt.xlabel('t')
plt.ylabel('y(t)')
plt.show()
รูป: ตัวอย่างการรันโค้ดใน Spyder IDE สำหรับตัวอย่างที่ 2
รูป: แสดงรูปกราฟสำหรับ ที่ได้จากการคำนวณเชิงสัญลักษณ์ด้วย SymPy
การหาผลเฉลยสำหรับสมการเชิงอนุพันธ์ที่มีอันดับมากกว่าหนึ่ง มีอีกวิธีหนึ่งคือ การแปลงสมการดังกล่าวให้เป็นระบบสมการอนุพันธ์ที่มีอันดับเป็นหนึ่ง ซึ่งมีแนวทางดังนี้
ขั้นตอนแรกคือ กำหนดฟังก์ชันเป็นตัวช่วย เช่น และ สำหรับสมการอนุพันธ์อันดับสอง
จากนั้นเขียนสมการอนุพันธ์ให้อยู่ในรูปเมทริกซ์
ตัวอย่างการเขียนโค้ด Python โดยใช้การคำนวณเชิงสัญลักษณ์ในรูปแบบเมทริกซ์และเวกเตอร์ มีดังนี้
from sympy import *
from sympy.solvers.ode.systems import dsolve_system
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display
# define a symbol: use t as an independent variable
t = symbols('t')
# define a function y(t)
y, z1, z2 = symbols('y z1 z2', cls=Function)
# compute the first and second derivatives of y(t)
D1y = Derivative(y(t),t,1)
D2y = Derivative(y(t),t,2)
# 2nd-order ODE: y'' + 2y' + y = sin(2t)
# Initial conditions: y(0)=-1, y'(0)=1
# define the ODE (system of 1st-order differential equations)
# [z1'(t), z2'(t)] = [ z2(t), f(t,z1,z2) ]
ode = [ Eq( z1(t).diff(t), z2(t) ),
Eq( z2(t).diff(t), -2*z2(t) -z1(t) + sin(2*t) ) ]
# set the initial condition: y(0) = 1.0
ics = { z1(0): -1, z2(0): 1 }
# solve the ODE with an initial condition (the IVP)
sol = dsolve( ode, ics=ics )
sol_eq = expand( sol[0].rhs )
display( sol_eq )
# convert the the solution into a callable function
f = lambdify( t, sol_eq )
# create an array of 100 time steps between [t0,tf]
t0 = 0.0
tf = 10.0
ts = np.linspace(t0,tf,100)
plt.clf()
plt.plot( ts, f(ts), 'o' )
plt.grid()
plt.xlabel('t')
plt.ylabel('y(t)')
plt.show()
รูป: ผลลัพธ์จากการรันโค้ดเพื่อสาธิตการใช้คำสั่ง dsolve()
ลองมาดูตัวอย่างการคำนวณด้วยวิธีเชิงตัวเลข โดยใช้คำสั่ง solve_ivp()
ของ SciPy
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# 2nd-order ODE: y'' + 2y' + y = sin(2t)
# Initial conditions: y(0)=-1, y'(0)=1
t0 = 0.0 # start point for t
tf = 10.0 # end point for t
h = 0.05 # step size
f = lambda t,z: [z[1], -2*z[1] - z[0] + np.sin(2*t) ]
ts = np.arange(t0,tf,h) # array of time steps
sol = solve_ivp( f, [t0,tf], [-1,1], t_eval=ts, method='RK45' )
plt.clf()
plt.plot( ts, sol.y[0], 'o' )
plt.grid()
plt.xlabel( 't' )
plt.ylabel( 'y(t)' )
plt.show()
รูป: แสดงรูปกราฟของฟังก์ชันที่เป็นผลเฉลยเมื่อใช้คำสั่ง solve_ivp()
▷ ตัวอย่างที่ 3#
ตัวอย่างถัดไปเป็นสมการเชิงอนุพันธ์อันดับ 4 พร้อมเงื่อนไขค่าเริ่มต้น
หากพิจารณาสมการที่เป็นเอกพันธ์ จะเขียนสมการลักษณะเฉพาะได้ดังนี้
อาจลองเขียนโค้ดและใช้คำสั่งของ SymPy ในการแก้สมการเพื่อหารากของสมการได้ดังนี้
s = symbols('s')
roots = solve( Eq(s**4 + 2*s**3 + 2*s**2 + 2*s + 1,0), s )
print(roots)
# => [-1, -I, I]
รากของสมการซึ่งมีดีกรีของพหุนามเท่ากับ 4 หาได้ดังนี้
ซึ่งจะเห็นได้ว่า มีค่าเป็นจำนวนจริงแต่ซ้ำกัน (repeated roots) และ เป็นจำนวนเชิงซ้อน (เป็นสังยุคของจำนวนเชิงซ้อนคู่กัน)
ดังนั้นรูปแบบของฟังก์ชันที่เป็นผลเฉลยของสมการเชิงอนุพันธ์ที่เป็นเอกพันธ์ เขียนได้ดังนี้
โดยที่ เป็นค่าคงที่ใด ๆ
ผลเฉลยเจาะจงสำหรับสมการเชิงอนุพันธ์ที่ไม่เป็นเอกพันธ์ ก็มีรูปแบบดังนี้
ดังนั้นเมื่อได้ผลเฉลยทั้งสองกรณีแล้ว ก็นำมาหาผลรวม
หากพิจารณาเงื่อนไขสำหรับค่าเริ่มต้น จะได้ผลเฉลยตามรูปแบบของฟังก์ชันดังนี้
ตัวอย่างการเขียนโค้ด Python เพื่อหาผลเฉลยเชิงตัวเลข มีดังนี้
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
# ODE:
# D4y + 2*D3y + 2*D2y + 2*D1y + y = 10*exp(-2*t);
# Initial conditions:
# y(0)==0; D1y(0)==1; D2y(0)==-1; D3y(0)==2
t0 = 0.0 # start point for t
tf = 20.0 # end point for t
h = 0.1 # step size
def f(t,z):
dz1 = z[1]
dz2 = z[2]
dz3 = z[3]
dz4 = -2*z[3] - 2*z[2] - 2*z[1] - z[0] + 10*np.exp(-2*t)
return [dz1,dz2,dz3,dz4]
def f_exact(t):
return (1/2)*np.exp(-t) + (6*t)*np.exp(-t) \
- (5/2)*np.cos(t) - (1/2)*np.sin(t) + 2*np.exp(-2*t)
ics = [0,1,-1,2] # vector of initial values
ts = np.arange(t0,tf,h) # array of time steps
# solve the ODE system numerically
sol = solve_ivp( f, [t0,tf], ics, t_eval=ts, method='RK45' )
plt.clf()
# plot the numerical solution compared with the analytic solution
plt.plot( ts, sol.y[0], 'o', ts, f_exact(ts) )
plt.grid()
plt.xlabel( 't' )
plt.ylabel( 'y(t)' )
plt.legend( ['numeric','analytic'] )
plt.show()
รูป: กราฟของฟังก์ชันที่เป็นผลเฉลย (ใช้ค่า )
▷ ตัวอย่างที่ 4#
ถัดไปเป็นการหาผลเฉลยของสมการเชิงอนุพันธ์ แต่จะลองเขียนโค้ด Python โดยใช้วิธีการที่เรียกว่า Runge-Kutta (4th-order)
สมการเชิงอนุพันธ์อันดับสองต่อไปนี้ มีฟังก์ชัน ที่เป็นผลเฉลยดังนี้
การคำนวณด้วยวิธีการของ Runge-Kutta (4th-order) #
วิธีการของ Runge-Kutta (4th-order) หรือ RK4 จะใช้สำหรับการผลเฉลยเชิงตัวเลขของสมการเชิงอนุพันธ์อันดับหนึ่ง และมีการกำหนดค่าเริ่มต้นตามรูปแบบต่อไปนี้
และการคำนวณจะเริ่มจากค่าเริ่มต้น เพื่อคำนวณค่าของ ในตำแหน่งถัดไปคือ สำหรับ ตามลำดับ และให้มีระยะห่างคงที่เท่ากับ (เรียกว่า step-size) หรือ
สูตรในการคำนวณสำหรับ RK4 สำหรับ มีดังนี้
ดังนั้นถ้าจะคำนวณค่า ในแต่ละรอบ จะต้องทราบค่าของ และ
ถ้าจะนำไปใช้กับสมการเชิงอนุพันธ์อันดับสูงกว่าหนึ่ง จะต้องมีการแปลงให้เป็นระบบของสมการเชิงอนุพันธ์อันดับหนึ่ง และเขียนสูตรการคำนวณ RK4 ได้ดังนี้
โดยที่ และ เป็นเวกเตอร์ เช่นเดียวกับ ที่ให้ค่าเป็นเวกเตอร์
ดังนั้นถ้าจะเขียนโค้ด Python สำหรับตัวอย่างที่ 4 โดยใช้ RK4 ก็ทำได้ดังนี้
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import math
t0 = 0.0 # start point for t
tf = 10.0 # end point for t
h = 0.2 # step size
def F(t,Y):
dY1 = Y[1]
dY2 =-2*Y[1] - Y[0] + math.exp(-t)
return np.array([dY1,dY2])
ics = [1.0,0.0]
ts = np.arange(t0,tf,h) # array of time steps
Y = np.zeros( (len(ts),2) );
Y[0,:] = np.array(ics)
for i in range(0,len(ts)-1):
K1 = F( ts[i] , Y[i,:] );
K2 = F( ts[i] + h/2 , Y[i,:] + h*K1/2 );
K3 = F( ts[i] + h/2 , Y[i,:] + h*K2/2 );
K4 = F( ts[i] + h , Y[i,:] + h*K3 );
Y[i+1,:] = Y[i,:] + h*(K1 + 2*K2 + 2*K3 + K4)/6;
f_exact = lambda t: (1/2)*np.exp(-t)*(t**2 + 2*t + 2);
Y = Y.T # transpose
plt.clf()
plt.plot( ts, Y[0], 'o', ts, f_exact(ts), '-' )
plt.grid()
plt.xlabel( 't' )
plt.ylabel( 'y(t)' )
plt.legend(['numeric','analytic'])
plt.show()
รูป: กราฟของฟังก์ชันที่เป็นผลเฉลยจากการใช้วิธี RK4 (ใช้ค่า )
▷ กล่าวสรุป#
บทความนี้ได้นำเสนอตัวอย่างการเขียนโค้ดสำหรับ Python เพื่อหาผลเฉลยของสมการเชิงอนุพันธ์สามัญ ด้วยวิธีการคำนวณเชิงสัญลักษณ์ และเปรียบเทียบกับวิธีการคำนวณเชิงตัวเลข
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Created: 2022-04-14 | Last Updated: 2022-04-29