集合のごくごく基礎(表面的なところ)を勉強・復習を兼ねてPythonとSymPyの各インターフェイスで扱っていきます。
※記事執筆者は理系出身ではなく数学や用語などで色々粗い点等はご容赦ください。
使うもの
- Python 3.9.0 (一部3.8や3.9など特有の型アノテーションの機能も使っています)
- SymPy 1.9
- Jupyter (VS Code上のものを利用)
※SymPyは以下のようなコマンドでインストールすることができます。
$ pip install sympy==1.9
また、SymPyの基本に関しては以前記事にしたのでそちらも必要に応じてご確認ください。
集合のPythonビルトイン上での扱い方
Pythonビルトインでは集合を表すには{}
の括弧を使います。辞書と同じ括弧ですが、(辞書のキーと値といったように)コロンは使わずにコンマ区切りで表現します。
たとえば10, 20, 30の3つの値からなる集合を作るには以下のように書きます。
set_value: set = {10, 20, 30}
print(set_value)
{10, 20, 30}
他の値、例えばリストなどから集合に変換するにはsetクラスを使ってキャストすると対応ができます。集合は同じ値を複数持たないため、もしリストなどの中に同じ値が含まれていれば1つのみ値が残ります。これを利用して集合をさらにリストなどに変換してビルトインのリストを一意(ユニーク)な値のみのリストに変換する際などにも(NumPyなどを通さない場合等に)利用されたりもします。
以下の例では10の値が複数含まれるリストを集合に変換していますが、結果的に10の値が1つのみ残っていることが確認できます。
set_value: set = set([10, 10, 10, 20, 30])
print(set_value)
{10, 20, 30}
SymPyでの集合の扱い方
SymPyではFiniteSetクラスを使います。コンストラクタに集合に設定する各値を指定します。
from sympy import FiniteSet
set_value: FiniteSet = FiniteSet(10, 20, 30)
Jupyter上で出力してみると以下のように表示されます。
set_value
ビルトインのsetクラスと同様にFiniteSetクラスでも重複している値は取り除かれ一意になります。
set_value: FiniteSet = FiniteSet(10, 10, 10, 20, 30)
set_value
複数の型(整数と浮動小数点数など)を含めたり、分数(Fractionクラス)の値などを含めたりもできます。余談ですが、SymPyでは他の数式表現などと同様に値や要素の順番は担保されません(ルールに従って順番が変わったりします)。以下の例では小さい値ほど前の方に配置されていることが確認できます。
from fractions import Fraction
set_value: FiniteSet = FiniteSet(10, 10.5, Fraction(2, 3), 5)
set_value
集合内に特定の値が含まれているかを調べる
ビルトインのsetでもSymPyのFiniteSetでもどちらもinを使って集合内に特定の値が含まれているかどうかを調べることができます(ただしFiniteSetの方はPylanceの型チェックなどでは引っかかるようで・・・SymPyの影響なので動作自体は問題無く動作します)。
set_value: set = {10, 20, 30}
20 in set_value
True
40 in set_value
False
set_value: FiniteSet = FiniteSet(10, 20, 30)
20 in set_value
True
40 in set_value
False
リストやタプルなどからFiniteSetを作る
ビルトインのリストやタプルなどからFiniteSetを作りたい場合にはPythonの言語仕様的に*
のアスタリスク記号をリストなどの前に配置して引数に渡すと各引数に位置引数として展開してくれるので対応ができます。
list_value: list[int] = [10, 20, 30]
set_value: FiniteSet = FiniteSet(*list_value)
set_value
空のFiniteSet
空の集合のインスタンスを作りたい場合にはFiniteSetのコンストラクタで引数を指定せずにインスタンスを作成します。Jupyter上で表示してみると∅という表示になります。0に見えますが0ではなく∅です。
set_value: FiniteSet = FiniteSet()
set_value
FiniteSetに対してループを回す
FiniteSetのインスタンスはそのままループでイテレーションを回すことができます。ただし前述の通り引数指定時と比べて順番が変わったりするケースもあるため、ループで渡される各値の順番には留意する必要があります。
set_value: FiniteSet = FiniteSet(3, 2, 1)
for value in set_value:
print(value)
1
2
3
FiniteSetの比較
FiniteSetはFiniteSet同士で比較演算子(==や!=など)を使うことができます。
set_value_1: FiniteSet = FiniteSet(1, 2, 3)
set_value_2: FiniteSet = FiniteSet(1, 2, 3)
set_value_1 == set_value_2
True
set_value_1: FiniteSet = FiniteSet(1, 2, 3)
set_value_2: FiniteSet = FiniteSet(1, 2)
set_value_1 == set_value_2
False
リストやタプル等とは異なりコンストラクタの引数で値の順番が異なっていても一致します。
set_value_1: FiniteSet = FiniteSet(1, 2, 3)
set_value_2: FiniteSet = FiniteSet(3, 2, 1)
set_value_1 == set_value_2
True
部分集合の判定を行う
部分集合(subset / 下位集合)かどうかの判定をしたい場合にはissubsetメソッドで真偽値を取ることができます。
例えば集合Aが集合Bの部分集合($A ⊆ B$)かどうかの判定は以下のように行います。
set_A: FiniteSet = FiniteSet(1, 2)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.issubset(set_B)
True
set_A: FiniteSet = FiniteSet(1, 4)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.issubset(set_B)
False
なお、このメソッドは各集合の値が同じでもTrueとなります。値が同じ場合には真部分集合の判定が必要となり別のメソッドを使うことが必要になります(後の節で触れます)。
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.issubset(set_B)
True
上位集合の判定を行う
上位集合(superset)の判定を行うにはissupersetメソッドを使います。部分集合(issubset)と逆の挙動をします。
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 2)
set_A.issuperset(set_B)
True
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 4)
set_A.issuperset(set_B)
False
真部分集合かどうかの判定を行う
部分集合の条件に加えて2つの集合の値が一致していない条件となる真部分集合(proper subset)かどうかの判定を行うにはis_proper_subsetメソッドを使います。
例えば集合Aが集合Bの真部分集合($A ⊆ B$ 且つ $A ≠ B$)かどうかを判定したい場合には以下のように書きます。
set_A: FiniteSet = FiniteSet(1, 2)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.is_proper_subset(set_B)
True
通常の部分集合と異なり2つの集合の値が一致している場合はFalseとなります。
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.is_proper_subset(set_B)
False
真上位集合かどうかの判定を行う
真部分集合の逆の処理となる真上位集合(proper superset)かどうかの判定を行うにはis_proper_supersetメソッドを使います。
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 2)
set_A.is_proper_superset(set_B)
True
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(1, 2, 3)
set_A.is_proper_superset(set_B)
False
FiniteSetの冪集合を求める
FiniteSetの集合の冪集合(power set)を求めるにはpowersetメソッドを使います。例えば{1, 2, 3}
という集合に対して使うと空の集合(
∅)、{1}
, {2}
, , ..., {1, 3}
..., {1, 2, 3}
といったように各集合の値が作られます。
set_value: FiniteSet = FiniteSet(1, 2, 3)
power_set_value = set_value.powerset()
power_set_value
作成される集合の件数は、元の集合の値の件数を$|s|$とすると$2^{|s|}$となります。つまり元の集合の値の件数が3であれば$2^3 = 8$となり、元の集合の件数が4であれば$2^4 = 16$となります。
set_value: FiniteSet = FiniteSet(1, 2, 3)
power_set_value = set_value.powerset()
len(power_set_value)
8
set_value: FiniteSet = FiniteSet(1, 2, 3, 4)
power_set_value = set_value.powerset()
len(power_set_value)
16
FiniteSetの和集合を求める
FiniteSetの和集合を求めるにはunionメソッドを使います。2つの集合をOR条件で統合したような集合を得ることができます(どちらかの集合に含まれる値は結果の集合に残ります)。数式上だと$∪$の記号を使って$A ∪ B$といったような表記がされたりします。
例えば{1, 2}
という値を持つ集合Aと{2, 3}
という値を持つ集合Bの和集合は{1, 2, 3}
という結果になります。
set_A: FiniteSet = FiniteSet(1, 2)
set_B: FiniteSet = FiniteSet(2, 3)
union_set = set_A.union(set_B)
union_set
FiniteSetの積集合を求める
FiniteSetの積集合(交差集合)を求めるにはintersectメソッドを使います。AND条件で統合したような集合を得ることができます(両方の集合に含まれる値のみ結果の集合に残ります)。数式上だと$∩$の記号を使って$A ∩ B$といったような表記がされたりします。
例えば{1, 2, 3}
という値を持つ集合Aと{2, 3, 4}
という値を持つ集合Bの積集合は{2, 3}
となります。
set_A: FiniteSet = FiniteSet(1, 2, 3)
set_B: FiniteSet = FiniteSet(2, 3, 4)
intersection_set = set_A.intersect(set_B)
intersection_set
デカルト積を求める
2つの集合のデカルト積(Cartesian Product)を求めるにはそれぞれの集合を*
の記号で計算すれば対応ができます。例えば{1, 2}
という集合と{3, 4}
という集合の2つのデカルト積を求めると(1, 3), (1, 4), (2, 3), (2, 4)
という4つのタプルが得られます。
set_A: FiniteSet = FiniteSet(1, 2)
set_B: FiniteSet = FiniteSet(3, 4)
cartesian_prod_set = set_A * set_B
cartesian_prod_set
Jupyter上では×の記号を使った表記になります。
値はループを回すことができ、それぞれの値をタプルで得ることができます。
for tuple_val in cartesian_prod_set:
print(tuple_val)
(1, 3)
(2, 3)
(1, 4)
(2, 4)
参考文献・参考サイトまとめ