SymPy で方程式(等式)を定義し,(主に代数)方程式を解く。関連して,Python のデータ型であるタプル (tuple) と辞書 (dict) について簡単に説明します。
SymPy の import
from sympy.abc import *
from sympy import *
init_printing()
方程式
SymPy は,式の計算だけでなく,代数方程式を解くことができます。例を以下に示します。
$x^2 + 2 x – 2 = 0$ を $x$ について解きます。
方程式の定義 Eq()
方程式とは,未知変数を含む等式のことです。
方程式は等式と同じく Eq(左辺, 右辺) のように定義します。(= は SymPy では代入を表します。)
eq = Eq(x**2 + 2*x - 2, 0)
eq
方程式の左辺 .lhs,右辺 .rhs
方程式 eq の左辺は eq.lhs (left-hans-side),右辺は eq.rhs (right-hand-side) のようにして取り出すことができます。
eq.lhs
eq.rhs
方程式を解く solve()
方程式を未知変数について解く書式は solve(方程式, 未知変数) です。
たとえば,方程式 eq を x について解く場合は solve(eq, x):
solve(eq, x)
方程式が 左辺 = 0 の形(つまり右辺がゼロ)になっている場合は,solve(方程式の左辺, 未知変数) としても同じです。
solve(x**2 + 2*x - 2, x)
3 次方程式 $x^3 – 8 = 0$ を解きます。solve(eq, x) の場合の x は省略して solve(eq) と書くこともできるようです。
sols = solve(x**3 - 8)
sols
解が複数個ある場合は,solve() は解のリストを返します。
念のため,3番目の答え(Python はゼロからはじまることに注意)を3乗して展開すると…
sols[2]**3
expand() でかっこを展開して計算させると…
expand(_)
連立方程式を解く solve([], [])
連立方程式を解く際は,方程式と変数をリストにして solve() に入れます。
solve([eq1, eq2, ..., eqn], [x1, x2, ..., xn])
例として
\begin{eqnarray}
x^2 + y^2 &=& 5 \\
x + y &=& 1
\end{eqnarray}
を $x, y$ について解きます。
eq1 = Eq(x**2 + y**2, 5)
eq2 = Eq(x + y, 1)
display(eq1, eq2)
solve([eq1, eq2], [x, y])
方程式の右辺を移項して
\begin{eqnarray}
x^2 + y^2 -5 &=&0 \\
x + y -1 &=&0
\end{eqnarray}
の形にすれば,各方程式の左辺のみを使って以下のように書いてもよいです。
sols12 = solve([x**2 + y**2 - 5, x + y - 1], [x, y])
sols12
2組の解がある場合,solve() は解 $(x, y)$ の組をリストで返します。
type(sols12)
display() による表示
上のコードでは確認の意味を込めて display() で方程式を表示させています。
print() による表示と display() による表示を比較してみてください。display() で表示させたほうが,綺麗に形が整えられて表示されるので,確認しやすいと思います。
print(eq1)
display(eq1)
display() を使わなくても,eq1 と書けば表示されますが,以下のように1つのセル内に複数行書くと,最後の行しか表示されません。
eq1
eq2
display() を使うと…
display(eq1)
display(eq2)
タプル (tuple):Python のデータ型
解のリスト sols12 の各要素は以下のように ( ) の中に複数の(今の場合は2個の)要素がカンマ (,) で区切られて並んでいます。
リストと似ていますが,[ ] の中に要素が並ぶリストと違い,( ) の中に要素が並ぶデータ型を「タプル (tuple)」と呼びます。(( ) なしでもタプル。)
for sol in sols12:
print(sol)
type() で確認します。
type(sols12[0]), type(sols12[1])
参考:タプル (tuple) と リスト (list) の違い
リストとは異なり,タプルは変更ができません。
リストの場合は,例えば lista の2番目の要素 lista[1] の値を別の値に置き換えることができます。
# リストの定義
lista = [1, 2, 3]
type(lista)
# lista の2番目の要素。Python はゼロ始まりだから...
lista[1]
# lista の2番目の要素の値を 20 に変更
lista[1] = 20
lista
しかし,タプルの場合は,例えば tupleb の2番目の要素 tupleb[1] の値を別の値に置き換えようとすると…
# タプルの定義
tupleb = 1, 2, 3
type(tupleb)
# tupleb の2番目の要素。
# リストの場合と同じように参照できる。
tupleb[1]
# tupleb の2番目の要素の値を変更しようとすると...
tupleb[1] = 20
エラーとなります。
複数変数への一時的代入
.subs()
念のため,solve() で得られた答えを eq1 の左辺 eq1.lhs に代入して確かに答えが $5$ になっているか,確認してみます。
.subs() による一時的代入は既に説明済みですが,複数の変数(今の場合は x と y)に値 x1, y1 を一時的に代入するには以下のように
.subs({x:x1, y:y1})
または,
.subs([(x, x1), (y, y1)])
print('(x, y) =', sols12[0])
(x1, y1) = sols12[0]
print('eq1 の左辺 =', eq1.lhs.subs({x:x1, y:y1}))
print('(x, y) =', sols12[0])
(x1, y1) = sols12[0]
print('eq1 の左辺 =', eq1.lhs.subs([(x, x1), (y, y1)]))
もう1組の解 (2, -1) についても確認してみて。
Subs()
一時的な代入を Subs(expr, variables, values).doit() でやってみます。
Subs() を使う場合は変数を tuple にできて
Subs(expr, (x, y), (x1, y1)).doit() のように書きます。
print('(x, y) =', sols12[0])
print('eq1 の左辺 =', Subs(eq1.lhs, (x, y), sols12[0]).doit())
もう1組の解 (2, -1) についても確認してみて。
例題:鶴亀算
例題
ツルとカメが合わせて8匹、足の数が合わせて26本であるとき、ツルとカメは何匹(何羽)いるか。ただしツルの足は2本、カメの足は4本である。
… とある。ツルを何匹とは数えないだろうから,
ツルとカメの個体数の合計は 8。足の数の合計が 26本。
ツルは何羽,亀は何匹?
ツルの個体数を tsuru,カメの個体数を kame として連立方程式にして解く。
なお,最初に
from sympy.abc import *
としていると,1文字変数については宣言する必要がないが,2文字以上の変数については,以下のように宣言してやる必要がある。
var('tsuru kame')
# 個体数の合計
eq1 = Eq(tsuru + kame, 8)
# 足の本数の合計
eq2 = Eq(2*tsuru + 4*kame, 26)
# 確認のため,display() で方程式を表示
display(eq1, eq2)
solve() で連立方程式を解きます。
sols = solve([eq1, eq2], [tsuru, kame])
print(sols)
辞書 (dict):Python のデータ型
同じ solve() で連立方程式を解いても,この場合の連立方程式の解 sols には,タプルではなく,{ } の中に 変数名 (key): 数値 (value) のような形でデータが格納されています。このようなデータ型を Python では「辞書 (dict)」と呼びます。
type() で確認します。
type(sols)
辞書 sols の中の,変数名 (key) kame や tsuru とペアになっている値 (value) を表示するには,以下のように。
print('カメは', sols[kame], '匹')
print('ツルは', sols[tsuru], '羽')
念のため,得られた解を方程式 eq1 の左辺に代入して,確かに $8$ になっていることを確かめます。
eq1
今の場合,解 sols は辞書型で与えられていますから…
type(sols)
方程式 eq1 の左辺 eq1.lhs に一時的に代入する .subs() の引数には sols をそのまま入れます。
eq1.lhs.subs(sols)
参考:出力される解のデータ型の違い
SymPy の solve() で連立方程式を解くと,得られた解のデータ型は
- 解が1組のみの場合は,辞書 (dict)
- 解が複数組ある場合は,タプル (tuple) のリスト (list)
となるようです。辞書かタプルかによって,得られた解の参照の仕方が異なりますので少しややこしい。
1 変数の方程式の解が複数ある例。解は数値・変数のリスト (list) で出力される。
solve(x**2 + 2*x - 2, x)
solve(x**2 + 2*b*x + c, x)
2 変数の連立方程式の解が 1 組だけの例。解は辞書 (dict) 型で出力される。
var('tsuru kame')
solve([Eq(tsuru + kame, 8),
Eq(2*tsuru + 4*kame, 26)], [tsuru, kame])
2 変数の連立方程式の解が 2 組ある場合。解はタプル (tuple) を要素としたリストで出力される。
solve([Eq(x**2 + y**2, 5), Eq(x + y, 1)], [x, y])
解のデータ型を辞書に統一する dict = True
solve() のオプション dict = True をつけると,解が全て(解が1個だけであっても)辞書のリストで出力されます。
解のデータ型が統一されると,解の参照の仕方も .subs() で統一できるので,ややこしさが減ります。
solve(x**2 + 2*x - 2, x, dict=True)
solve(x**2 + 2*b*x + c, x, dict=True)
var('tsuru kame')
solve([Eq(tsuru + kame, 8),
Eq(2*tsuru + 4*kame, 26)],
[tsuru, kame], dict=True)
solve([Eq(x**2 + y**2, 5), Eq(x + y, 1)],
[x, y], dict=True)
○練習:鶴と亀と蟻
鶴と亀と蟻,個体数の合計は 10。足の数は全部で 34 本。蟻は亀より 1 匹少ない。鶴,亀,蟻,それぞれ何羽・何匹?
鶴と亀と蟻の個体数をそれぞれ tsuru, kame, ari として連立方程式をたてて solve します。蟻の足は何本かわかりますよね?
かつて線形代数の授業でこの問題を出したとき,真っ先に出た質問が「先生,蟻の足は何本ですか?」でした… orz
これに懲りて,次年度からは問題を一新!「蟻」のかわりに,「かぶと虫」にしたら(なぜか)正答率が上がりました!