プログラミング演習(2) - Pythonの微分方程式の解き方まとめ
記事ディレクトリ
概要
最近、Python を使用して微分方程式を解く必要があり、インターネット上で多くの情報や投稿を見つけ、個人的なアイデアに基づいていくつかの研究を行いました。
このブログでは、主に係数が一定の 2 階微分方程式の解析解と数値解、係数が一定の 1 階微分方程式の一般解を説明し、各ステップを詳しく説明します (インターネット上の多くの投稿はあまり詳しく説明されていません) )、いくつかの探索的な試みを行いました
コード分析
係数が一定の二次等次微分方程式の解析解
dsolve で分析解を取得します
ここで使用するライブラリは sympy です。
sympy.dslove 定係数同次微分方程式コードを使用するアイデアは次のとおりです。
- まず、必要な微分方程式をg ( f ( x ) , x ) = 0 g(f(x),x)=0に変換する必要があります。g ( f ( x ) 、× )=0の形式。
- 次に、導出される可能性のあるすべての独立変数を見つけ、同時に従属変数を決定し、sympy.symbols を使用して独立変数をマークし、Function を使用して従属変数を表します。
- 方程式を解いて出力する
例: 微分方程式f ( x ) '' + ω 2 f ( x ) = 0 f(x)''+\omega^2f(x)=0 を解きます。f ( x )「」+おお2 f(x)=0
次に、上記の簡単な 3 ステップのアプローチに基づいた例を見てみましょう。
import sympy as sy
x = sy.symbols("x") # 标出x和ω为自变量并用符号'x'和'w'表示之
omega = sy.symbols("w")
f = sy.Function("f") # 函数因变量f(x),用f表示
equation = f(x).diff(x,2) + omega**2*f(x) # 化成g(f(x),x) = 0的形式,diff是求导函数
print(sy.dsolve(equation,f(x)))
sy.pprint(sy.dsolve(equation,f(x)))# 两种输出方法,前面一个是返回的真实的dsolve返回的元组,后面是sympy库自带的格式化输出解析解的字符串
出力結果:
Eq(f(x), C1*exp(-I*w*x) + C2*exp(I*w*x))
-ⅈ⋅w⋅x ⅈ⋅w⋅x
f(x) = C₁⋅ℯ + C₂⋅ℯ
この結果は、対応する三角関数形式に変換できます。
それをチェックしてください
『同済上級数学書』の 346 ページにあるサンプル問題をテストしてみましょう。方程式y ( 4 ) − 2 y '' '' + 5 y '' '' = 0 y^{(4)}-2y''+ を求めてください。5y''=0y( 4 )−2年「」_+5年「」=0一般的な解決策。
コード:
import sympy as sy
x = sy.symbols("x")
y = sy.Function("y")
equation = y(x).diff(x,4)-2*y(x).diff(x,3)+5*y(x).diff(x,2)
print(sy.dsolve(equation, y(x)))
sy.pprint(sy.dsolve(equation, y(x)))
出力:
Eq(y(x), C1 + C2*x + (C3*sin(2*x) + C4*cos(2*x))*exp(x))
x
y(x) = C₁ + C₂⋅x + (C₃⋅sin(2⋅x) + C₄⋅cos(2⋅x))⋅ℯ
比較本の答えは正しいです。
dsolve は 2 次の不均一微分方程式を解くことができますか?
私たちは依然として同済高度数学書を使用してサンプル問題をテストしています。 方程式y '' − 5 y '' + 6 y = xe 2 x y''-5y'+6y=xe^{2x} の一般解を求めます。y「」−5年』+6歳=× e2倍_
コード:
import sympy as sy
from math import e
x = sy.symbols("x")
y = sy.Function("y")
equation = y(x).diff(x,2)-5*y(x).diff(x,1)+6*y(x)-x*e**(2*x)
print(sy.dsolve(equation, y(x)))
sy.pprint(sy.dsolve(equation, y(x)))
出力結果:
Eq(y(x), -0.5*7.38905609893065**x*x**2 - 1.0*7.38905609893065**x*x + C1*exp(2*x) + C2*exp(3*x))
x 2 x 2⋅x 3⋅
y(x) = - 0.5⋅7.38905609893065 ⋅x - 1.0⋅7.38905609893065 ⋅x + C₁⋅ℯ + C₂⋅ℯ
x
謎の出力、C 1 ∗ exp ( 2 ∗ x ) + C 2 ∗ exp ( 3 ∗ x ) C1*exp(2*x) + C2*exp(3*x) のみC1 _∗e x p ( 2∗× )+C2 _∗e x p ( 3∗x )この部分は答えと同じですが、他の長い数字は非常に混乱を招きます。
先頭に奇妙な数字が並んでいる理由は、アルゴリズムに関係している可能性がありますが、ここでは説明しませんが、dslove を使用して非一次方程式を解くのはそれほど簡単ではないとだけ言えます。
odeint+draw で数値解を求める
ここで数値解を見つけるために使用するライブラリは scipy と呼ばれます。
scipy.odeint の数値解法の背後にある考え方は次のとおりです。
- 2 次方程式の場合は、2 つの 1 次方程式から構成される連立方程式に変換する必要がありますが、1 次方程式の場合はこの手順をスキップしてください。
- 結果として得られる各連立方程式をy ' = f ( x , y ) y'=f(x,y)に変換します。y』=f ( x ,y )形式。ここで、x と y は各方程式に関連しており、これについては後で詳しく説明します。
- 数値解が必要なのでパラメータ値や初期条件を決める必要がありますが、odeintにはデフォルトでy=0 y=0が入ります。y=0は従属変数の値です。
- 独立変数の範囲を決定し、方程式を解き、グラフを描画します。
例を見てみましょう (scipy 公式 Web サイトのドキュメントの例): 微分方程式y '' + b ∗ y '' + c ∗ siny = 0 y''+b*y'+c*siny = 0 を解くy「」+b∗y』+c∗そうだね_=0、ここで、 b と c は t に関する導関数のパラメーターです。
上記の考え方に従って、まず 2 つの 1 次方程式に変換します。
y ' ( t ) = z ( t ) (1) y'(t)=z(t) \tag 1y( t )_=z ( t )( 1 )
z ' ( t ) = − b ∗ z ( t ) − c ∗ sin ( y ( t ) ) (2) z'(t)=-b*z(t)-c*sin(y(t)) \タグ2z( t )_=− b∗z ( t )−c∗s i n ( y ( t ) )( 2 )
2番目のxとyは相対式であり、式(2)を例にとると、z'(t)をy'、z(t)をy、tをx、 other はパラメータとして使用され、y ' = f ( x , y ) y'=f(x,y)になります。y』=f ( x ,y )、この odeint を使用して方程式を解くことができるようにします。
次に、コードとコメントを見てください。コメントでは基本的なメソッドのみが説明されています。
import matplotlib.pyplot as plt
import scipy.integrate as sp
import numpy as np
#设定参数b,c初值,其实这个方程是物理学中跟钟摆重力角和摩擦力有关的一个微分方程
b = 0.25
c = 5.0
def function(t, y):#设定两个参数,约定第一个为自变量t,第二个为相对每个方程的因变量y
yt, zt = y #规定每个方程的因变量
return [zt, -b * zt - c * np.sin(yt)]#返回一个向量,每一项分别是每个方程的右半部分
t = np.linspace(0, 10, 100)#规定数值解的范围
init_x = [np.pi - 0.1, 0]#规定初值,传入的是一个向量,分别是每个方程的y=0时t的取值
result = sp.odeint(function, init_x, t, tfirst=True)#解方程,注意第一个参数只填函数名不要写括号,tfirst是指是否把求导自变量作为函数第一个参数
#画图部分
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False
plt.xlabel('t')
plt.ylabel('f(t)')
plt.plot(t, result[:, 0], label='f:yt')#原函数数值解
plt.plot(t, result[:, 1], label='f:zt')#一阶导
plt.legend()
plt.show()
次に、ドキュメントを参照して、コードの重要な部分の詳細を説明しました。
- 1 つ目はカスタム関数です。この関数は odeint の必要なパラメータとして使用する必要があります。odeint を呼び出すと、odeint は定義した関数を自動的に呼び出します。
- カスタム関数の戻り値は配列ベクトルです。最初の項目は元の関数 (方程式の解) の 1 次導関数を表し (ここでは別の記号 zt を使用してそれを表します)、2 番目の項目は 2 次導関数を表します。元の関数の (つまり、上記の式 (2)) など...
- 数値解を求めているため、各方程式の初期値を指定する必要があります。ベクトルの第 1 項は最初の方程式の初期値などとなります。ここでの odeint のデフォルトの初期値は 0 ですが、これは h0 パラメータを通じて変更できます。詳細については、https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html を参照してください。
方程式の数値解の得られた視覚化結果は次のとおりです。
公式ウェブサイトの答えを比較すると、それらが正しいことがわかります。
連立微分方程式を解く
一次方程式を解く
まだ dsolve を使用していますが、プロセス全体は方程式を解くのと似ているので、直接例に進みましょう。
引き続き、高度な数学の本の質問例を使用して検証します (357 ページ)。
求微分方程组:
{ x ′ ( t ) = 3 ∗ x ( t ) − 2 ∗ y ( t ) y ′ ( t ) = 2 ∗ z ( t ) − y ( t ) \begin{cases} x'( t)=3*x(t)- 2*y(t) \\ y'(t)=2*z(t)-y(t) \end{cases}{
バツ( t )_=3∗x ( t )−2∗y ( t )y( t )_=2∗z ( t )−y ( t )
コード:
import sympy as sy
t = sy.symbols("t")
x = sy.Function("x")
y = sy.Function("y")
equation = x(t).diff(t,1)-3*x(t)+2*y(t)
equation2 = y(t).diff(t,1)-2*x(t)+y(t)
print(sy.dsolve([equation,equation2], [x(t),y(t)]))
sy.pprint(sy.dsolve([equation,equation2], [x(t),y(t)]))
もう 1 つの関数関係と 1 つの方程式を追加し、方程式を解くときに、2 つの方程式と 2 つの従属変数を配列に書き込み、対応する位置に渡すだけで、ひょうたんに従うだけです。次のように:
[Eq(x(t), (2*C1 + 2*C2*t + C2)*exp(t)), Eq(y(t), (2*C1 + 2*C2*t)*exp(t))]
⎡ t t⎤
⎣x(t) = (2⋅C₁ + 2⋅C₂⋅t + C₂)⋅ℯ , y(t) = (2⋅C₁ + 2⋅C₂⋅t)⋅ℯ ⎦
本の答えとは少し異なりますが、よく見てみると同等であることがわかります。
二次方程式系を解くことができますか?
高等数学の本の 358 ページにある例 2 を直接見てください。
import sympy as sy
from math import e
t = sy.symbols("t")
x = sy.Function("x")
y = sy.Function("y")
equation = x(t).diff(t,2)+y(t).diff(t,1)-x(t)-e**t
equation2 = y(t).diff(t,2)+x(t).diff(t,1)+y(t)
print(sy.dsolve([equation,equation2], [y(t),x(t)]))
sy.pprint(sy.dsolve([equation,equation2], [y(t),x(t)]))
このコードはエラーを報告することがわかります。
File "D:\Anaconda3\Anaconda\lib\site-packages\sympy\solvers\ode\ode.py", line 599, in dsolve
raise NotImplementedError
NotImplementedError
haha が実装されていないと書かれていたので、ode.py ファイルを調べて、このファイルで解ける微分方程式の種類を確認しました。
allhints = (
"factorable",
"nth_algebraic",
"separable",
"1st_exact",
"1st_linear",
"Bernoulli",
"Riccati_special_minus2",
"1st_homogeneous_coeff_best",
"1st_homogeneous_coeff_subs_indep_div_dep",
"1st_homogeneous_coeff_subs_dep_div_indep",
"almost_linear",
"linear_coefficients",
"separable_reduced",
"1st_power_series",
"lie_group",
"nth_linear_constant_coeff_homogeneous",
"nth_linear_euler_eq_homogeneous",
"nth_linear_constant_coeff_undetermined_coefficients",
"nth_linear_euler_eq_nonhomogeneous_undetermined_coefficients",
"nth_linear_constant_coeff_variation_of_parameters",
"nth_linear_euler_eq_nonhomogeneous_variation_of_parameters",
"Liouville",
"2nd_linear_airy",
"2nd_linear_bessel",
"2nd_hypergeometric",
"2nd_hypergeometric_Integral",
"nth_order_reducible",
"2nd_power_series_ordinary",
"2nd_power_series_regular",
"nth_algebraic_Integral",
"separable_Integral",
"1st_exact_Integral",
"1st_linear_Integral",
"Bernoulli_Integral",
"1st_homogeneous_coeff_subs_indep_div_dep_Integral",
"1st_homogeneous_coeff_subs_dep_div_indep_Integral",
"almost_linear_Integral",
"linear_coefficients_Integral",
"separable_reduced_Integral",
"nth_linear_constant_coeff_variation_of_parameters_Integral",
"nth_linear_euler_eq_nonhomogeneous_variation_of_parameters_Integral",
"Liouville_Integral",
)
確かに2次の定係数微分方程式というカテゴリはありませんが、私のsympyのバージョンは1.6.2ですが、新しいバージョンでこの機能が追加されるかどうかはわかりません。
概要: Python は微分方程式を解くのに便利で、ほとんどの基本的なニーズを満たすことができます (単純な数学モデリング方程式は基本的に問題ありません)。より高度なソリューションが必要な場合は、MATLAB に切り替えることをお勧めします。。。