Python 3 で数学を。

Python 3 とライブラリで数学の問題を解いていきます。統計学や機械学習はときどき。

Python (Python 3) で数学をやるにはどうしたらいいか。その19. 順列。"俺を見つけたければいつだって列の最後にいるぜ!"

このシリーズの過去記事は以下にまとめてある

py3math.hatenablog.com

当記事について

当ブログ筆者 (以下、筆者) も、Python で数学をやるにはどうしたらいいか悩んでいた時期があるから、昔の自分に向けて書いたような記事。

対象とする読者

Python の入門書を一冊か二冊、一通りやった人で、Python の基本的な構文や基本的な用語がだいたいわかっている人。

そして、公式サイトを読む努力をちゃんとする人。

順列をやってみよう

順列の基本的なことを SymPy でやってみよう。

では、早速、Python の対話モードから、

>>> from sympy.functions.combinatorial.numbers import nP

と打ち込んでみよう。

これで SymPy の nP() をすぐに使用できる準備ができた。

nP() を使用する前に、順列の数式を書いておく。

nPr = n! / (n - r)! だ。

それでは、まず、6P3nP() で求めてみよう。

>>> nP(6, 3)
120

1 つわかれば後は一括処理できることが想像できるだろう。

ここでは、6P310P2 をリスト内包表記で一括処理する。

>>> p_list = [[6, 3], [10, 2]]
>>> res1 = [nP(*i) for i in p_list]
>>> res1
[120, 90]
>>> print(*res1)
120 90

では、さきに書いた数式を使用して nP() を自作関数として作成してみよう。

まず階乗を計算する自作関数から作成する。

>>> def my_factorial(n):
...     if n < 2:
...         return 1
...     else:
...         return n * my_factorial(n-1)
...
>>> my_factorial(10)
3628800

そして、my_factorial() を使用して、my_nP() を数式通りに作成してみる。

def my_nP(n, r):
    return my_factorial(n) / (my_factorial(n-r))

my_nP() を使用して、6P3 を求めてみよう。

>>> my_nP(6, 3)
120.0

1 つわかれば後はリスト内包表記で一括処理できる。

>>> res2 = [my_nP(*i) for i in p_list]
>>> res2
[120.0, 90.0]
>>> print(*res2)
120.0 90.0

上ではリスト内包表記を使用したが、for 文 がよいと思うならぜひ書き直してみてほしい (for 文のほうが出力を整えやすいから、読みやすいものにしてみてほしい)。

さて、my_nP() は小数として出力されている。SymPy の nP() はそうではなかった。

簡単に調べてみよう。

SymPy の nP():

>>> x1 = nP(6, 3)
>>> type(x1)
<class 'sympy.core.numbers.Integer'>

my_nP():

>>> x2 = my_nP(6, 3)
>>> type(x2)
<class 'float'>

自作関数の my_nP()int にするにはどうしたらいいだろうか。

ぜひ考えてみてほしい (もっとも、このままでよいと思うならそれでよい)。

ここで、少しだけ SciPy に慣れておこう (マシンにすでに SciPy が入っていることを前提に話を進めていく)。

Python の標準ライブラリにも factorial() はあるが、SciPy の factorial() を使用してみよう。

以下のようにしてインポートする。

>>> from scipy.special import factorial

簡単に使ってみる。

>>> factorial(10)
array(3628800.)
>>> print(factorial(10))
3628800.0

SciPy の factorial() を使用して、my_nP() を書き換えてみよう。

>>> def my_nP(n, r):
...     return factorial(n) / (factorial(n-r))
...
>>> my_nP(6, 3)
120.0

これまでと同じように、リスト内包表記で一括処理してみる。

>>> res3 = [my_nP(*i) for i in p_list]
>>> res3
[120.0, 90.0]
>>> print(*res3)
120.0 90.0

SciPy を少しだけ使用したわけだが、筆者は個人的に、記号を使用する場合には SymPy を使用する、それ以外は SciPy や NumPy を使用する、だが、SymPy に使い勝手のよい関数やメソッドやクラスが用意されている場合には SymPy を使用する、としている。

今後、当シリーズでは、SymPy だけでなく、SciPy や NumPy が少しずつ登場してくる。

(つづく)。

参考文献 (数式を参考)