読者です 読者をやめる 読者になる 読者になる

ばぐばぐわーるど

Pythonなどなど

Pythonで学ぶ数学 集合編

この前の心意気虚しく紹介したページの内容全然分かりませんでした。数学以前にPython力が足りてなかったです。抽象基底クラスとかイミフです。

というわけで、まずは簡単な数学からやってレベルを上げてくことにします。
とりあえず適当に作った問題をPythonで解いていきます。問題は一部、すぐわかる代数(東京図書)を参考に作りました。
今回は基本ということで集合から。


問題1 部分集合

 A = \{n | n = 12m , 0 < m  < 5 \}
 B = \{n | n = 6m , 0 < m < 10 \}
このとき
 A \subseteq B
を証明せよ。

ほんとは  m \in \mathbb{Z} がやりたかったのですが、まあ無理なので有限の集合にしときました。

解答

A = {12*n for n in range(1,5)}
B = {6*n for n in range(1,10)}
print A.issubset(B)

リスト内包表記は集合を表すset型({}のやつ)でも使えます。数学とほとんど表記が一緒なので分かりやすいです。
 B = \{6n | 0 < n < 10 \}
これならもっと近いです。数学的にこういう書き方(左側が素の文字じゃない)していいのか知らないですけど。意味は通じるのでたぶんあり?

3行目は A is subset of B で、AはBの部分集合(subset)だって主張です。真偽値を返します。

実行結果
True

というわけで証明できました。証明・・・?


問題2 直積

 A = \{ 1, 2, 3 \}
 B = \{ 2, 4, 6 \}
のときABの直積 A \times Bを求めよ。

ちなみに直積は数学ではこう書きます。
 A \times B = \{ (a,b) | a \in A \wedge  b \in B \}

解答(3通り)

def tyokuseki(A,B):
    li = []
    for x in A:
        for y in B:
            li.append((x,y))
    return li 

def gentyoku(A,B):
    for x in A:
        for y in B:
            yield (x,y)

A = range(1,4) 
B = range(2,7,2)

print tyokuseki(A,B)
print [x for x in gentyoku(A,B)]
print [(a,b) for a in A for b in B]
実行結果
[(1, 2), (1, 4), (1, 6), (2, 2), (2, 4), (2, 6), (3, 2), (3, 4), (3, 6)]
[(1, 2), (1, 4), (1, 6), (2, 2), (2, 4), (2, 6), (3, 2), (3, 4), (3, 6)]
[(1, 2), (1, 4), (1, 6), (2, 2), (2, 4), (2, 6), (3, 2), (3, 4), (3, 6)]

無駄に3つも載せました。思いついた順です。一つ目は普通に繰り返し、二つ目はジェネレータとリスト内包、三つ目はネストしたリスト内包表記です。
最後のネストしたリスト内包表記は数学の表記に近いですね。おまけに楽だし。


問題3 集合の演算

 X + Y \equiv X \cup Y
とする。
 A = \{ a,b,c \}
 B = \{ c,d,e \}
のとき
 A + B
を求めよ。

+(たす)は今までの意味とか考えずに、新しい演算として定義されていることに注意です。
関数でもできるんでしょうが、ここはせっかくなので演算子のオーバーロードでやりたいところですね。

解答

class Cup(object):
    def __init__(self, a):
        self.a = a

    def __add__(self, other):
        return self.a | other.a

set1 = {"a","b","c"}
set2 = {"c","d","e"}

A = Cup(set1)
B = Cup(set2)

print A+B
実行結果
set(['a', 'c', 'b', 'e', 'd'])

set型は | で和集合を取ります。通常+になってる部分を | に変えました。


補足 演算子のオーバーロードとは何か

上の例で行くとA+Bの定義を変えるのを、__add__という特殊メソッドを上書きしてしまうことで実現することです。
他の簡単な例も考えてみます。

  • 足し算を引き算にする。
class Gyaku:
    def __init__(self, x):
        self.x = x

    def __add__(self, other):
        return self.x - other.x 

go = Gyaku(5)
san = Gyaku(3)

print go + san 
実行結果
2

go + sanと書くとgo.__add__(san)が呼び出されます。
go.x - san.x が返されるわけです。

  • ベクトルの計算(足し算のみ)をする。
class Vector(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x , self.y + other.y)

v1 = Vector(1,5)
v2 = Vector(2,6)

V = v1+v2
print V.x, V.y 
実行結果
3 11

この場合はベクトル型が返されてるので、V.xとV.yで数値を取り出して表示してます。


なんというか、Pythonで学ぶ数学というより、数学で学ぶPythonですが今回はこんなところで。