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
これに懲りて,次年度からは問題を一新!「蟻」のかわりに,「かぶと虫」にしたら(なぜか)正答率が上がりました!