python sf は、普段のメモ書き数式そのままを計算させてたくて作りました。その python sf は 群論での数式にも使えます。
例えば対称群 Sn(4) における S4(3,0,2,1) 要素の共役類を下のように、エディタでメモ書きする数式に近い python sf 式で計算できます。
{g S4(3,0,2,1) g^-1 for g in SS4} # SS4 is a set of Sn(4) =============================== set([Sn(4)(3, 0, 2, 1), Sn(4)(1, 2, 0, 3), Sn(4)(1, 3, 2, 0), Sn(4)(2, 0, 1, 3), Sn(4)(0, 2, 3, 1), Sn(4)(3, 1, 0, 2), Sn(4)(0, 3, 1, 2), Sn(4)(2, 1, 3, 0)])
これは、下の数学でのメモと殆ど同じでしょう。
{g S4(3,0,2,1) g^-1 for g ∈ SS4} # SS4 is a set of Sn(4)
python sf により、記号の羅列に埋没しがちな抽象群での考察を、手間をかけずに具体例を交えたものにできます。誤りの入りやすい具体的な群論計算はコンピュータに押し付け、群論での多様な考察に専念できます。群論の具体例の計算は python sf 数式を書いていくだけです。プログラム開発をするわけではないので、デバッグ作業に手間をとられることもありません。
以下、通常の群論の教科書を呼んでいくときに行われる、 python sf を使った具体例での検討・考察の様子を見ていきます。
python sf では分野ごとに異なる 変数/関数名を、カレント・ディレクトリに設ける sfCrrntIni.py ファイルに記述しておきます。sfCrrntIni.py ファイルに宣言・定義しある変数は import することなく python sf 式で記述できます。
この群論での議論は、下の sfCrrntIni.py がカレント・ディレクトリに置かれていることを前提としています。これにより対称群 Sn(3),Sn(4),Sn(5) インスタンスを生成するクラスを S3,S4, S5, S6 の名前で扱えるようにしています。SS3,SS4,SS5 により、これらの対称群の集合を表しています。それらの交代群の集合は SA3,SA4,SA5 で表せます。また Sn(4) の部分具でもある正四面体群の辞書コンテナを dctP4 で扱えるようにしています。
type sfCrrntIni.py import pysf.octn as oc S5 = oc.Sn(5) SS5= S5.setStt SA5=set([S5(x) for x in S5.m_tplIdxStt[:5*4*3*2//2] ]) S4 = oc.Sn(4) SS4= S4.setStt SA4=set([S4(x) for x in S4.m_tplIdxStt[:5*4*3*2//2] ]) S3 = oc.Sn(3) SS3= S3.setStt SA3=set([S3(x) for x in S3.m_tplIdxStt[:3*2//2] ]) S6 = oc.Sn(6) dctP4 = { 'e':S4(0,1,2,3) , 'a1':S4(0,2,3,1),'b1':S4(0,3,1,2) , 'a2':S4(3,1,0,2),'b2':S4(2,1,3,0) , 'a3':S4(1,3,2,0),'b3':S4(3,0,2,1) , 'a4':S4(2,0,1,3),'b4':S4(1,2,0,3) , 'hx':S4(2,3,0,1),'hy':S4(1,0,3,2),'hz':S4(3,2,1,0)} dctP4I = dict(zip(dctP4.values(), dctP4.keys()))
もっと高度な群論計算をさせたい方は、それに対応するモジュールを持ってくるなり作るなりして sfCrrntIni.py の中で import してやれば python sf から使えるようになります。下の一行を sfCrrntIni.py の中に挿入するだけで、虹鱒の名で配布されている python 群論モジュールを使えるようになります。
import nzmath as nz
まず半群についての python sf を使った具体的な検討・考察例を見ていきましょう。
半群であるとは推移律: (x y) z == x (y z) が成り立つことです。これは部分的に演算を定めると、それが別の演算まで定めてしまうことを意味します。演算規則の定まり方に粘着性があることを意味します。
python の辞書データ型を使えば、この半群の性質を記述できます。実際に推移律演算を実行させられます。以下では具体的に a b c d 四文字の集合の半群構造を python の辞書を使って記述・実行していきます。
集合 G が半群であるとは、下のような関数を定義できることです。
f:G x G --> G f(f(x,y),z) == f(x, f(y,z) ) for ∀ x,y,z ∈ G
{(g0,g1):g3, .... } where g0,g1,g3, ... ∈ G
具体例で考えましょう。キャラクタ a b c d を要素とする集合 G に対して、python 辞書を使って下のように半群の演算を含んだ辞書 f を構築できます。
f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'} =============================== {('a', 'b'): 'd', ('a', 'a'): 'b', ('a', 'd'): 'c', ('a', 'c'): 'a'}
この辞書では f['a','a'] は 'b' の値を持ちます。これは f('a','a') == 'b' と同じ意味だと解釈できます。この関数ともみなせる辞書は実際に f GxG --> G を計算可能です。
f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'}; f['a','a'] =============================== b
Python sf での 計算結果のデフォルト出力Python sf では複数の Python sf 式を「;」で区切って記述します。ユーザーが知りたい計算結果は、最後の Python sf 式の値であることが大部分です。ですから最後の Python sf 式の値は print 命令が無くても自動的にコンソールに出力します。
上の f GxG --> G を計算する Python sf 式では「f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'}」の辞書コンテナ f を定義する Python sf 式に続けて、この 辞書コンテナ f を使った計算「f['a','a']」の Python sf 式を二つ目・最後に書いています。この計算結果は自動的にコンソールに「===============================\nb」文字列として打ち出されます。
なお、最後の Python sf 式の値は、カレント・ディレクトリの _dt.pvl ファイルにも出力されています。
type _dt.pvl # python object printed out by pprint 'b'「:=」記号を使えば、明示的にカレント・ディレクトリへの出力先ファイル名を指定できます。例えば、f 辞書コンテナを下のように f.pvl ファイルに出力できます。これらのカレント・ディレクトリに出力された値はファイル変数と読んでいます。
f:={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'} =============================== d「=:」記号を使えば、カレント・ディレクトリにファイル変数として残っている計算結果を再利用できます。
=:_dt,f;(f['a','b'],f['a','b']) =============================== ('d', 'd')_dt の値は常に Python sf 式の デフォルト最終値であり、変換し続けています。これらのファイル変数を使って、以前に行った計算結果を再利用していきます。
この辞書は演算表の a に対応する横一列を定めています。
\ 'a', 'b', 'c', 'd' a ['b', 'd', 'a', 'c']
実際に、この横一行全部を、Python のリスト内包表記構文を使った下の python コードで計算できます。
f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'}; [f['a', y] for x,y in sorted(f)] =============================== ['b', 'd', 'a', 'c']
次の節で f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'} 辞書を何度も使うので fDct.pvl ファイル変数を作っておきます。
fDct:={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'} =============================== {('a', 'b'): 'd', ('a', 'a'): 'b', ('a', 'd'): 'c', ('a', 'c'): 'a'}
このファイル変数 fDct.pvl を使えば、先の横一行の計算式は、下のように短くできます。
f=:fDct; [f['a', y] for x,y in sorted(f)] =============================== ['b', 'd', 'a', 'c']
半群であることより、演算表の一行目が定まると、それが他の行にも影響が与えてしまいます。演算表の一行目は勝手に決められますが、二行目以降には制限が加わります。
f('a', x) for ∀x ∈ G が定まりました。f('a', x) ∈ G です。ならば f('a', f('a', x) ) も定まります。 半群であることより f('a', f('a', x) ) == f(f('a','a'),x) == f('b', x) が定まることになるからです。f('a','a') == 'b' であることは、演算表の一列目から決まります。
半群の性質:推移律より、f(b,x) に対応する演算表の二行目は次のようにならねばなりません。
f=:fDct; [f['a',f['a', y]] for x,y in sorted(f)] =============================== ['d', 'c', 'b', 'a']
\ 'a', 'b', 'c', 'd' a ['b', 'd', 'a', 'c'] b = a a ['d', 'c', 'b', 'a']
同様に演算表の三行目:f(d,x) は次のようにならねばなりません。
f=:fDct; [f['a',f['a',f['a', y]]] for x,y in sorted(f)] =============================== ['c', 'a', 'd', 'b']
\ 'a', 'b', 'c', 'd' a ['b', 'd', 'a', 'c'] b = a a ['d', 'c', 'b', 'a'] d =a a a ['c', 'a', 'd', 'b']
同様に演算表の四行目 f(c,x) は次のようにならねばなりません。
f=:fDct; [f['a',f['a',f['a',f['a', y]]]] for x,y in sorted(f)] =============================== ['a', 'b', 'c', 'd']
\ 'a', 'b', 'c', 'd' a = a ['b', 'd', 'a', 'c'] b = a a ['d', 'c', 'b', 'a'] d = a a a ['c', 'a', 'd', 'b'] c =a a a a ['a', 'b', 'c', 'd']
半群であることにより、演算表の一行目を定めただけで、演算表の他の全てまで定まってしまいました。演算表の最後の行は、'c' 要素が単位元であることを示しています。 f={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'c'};G={'a','b','c','d'};all([f[x,y] == f[y,x] for x,y in mitr(G,G)])
演算表の一行目を定めただけで、演算表の残りの全てが定まるとは限りません。恒等変換が出てくるとも限りません。実際、半群の演算表の一行目の値を b,d,a,b としてやれば、下のような演算表になります。演算表の半分しか決まりませんし、恒等変換も出てきません。
fTmp:={('a','a'):'b', ('a','b'):'d', ('a','c'):'a', ('a','d'):'b'} =============================== {('a', 'b'): 'd', ('a', 'a'): 'b', ('a', 'd'): 'b', ('a', 'c'): 'a'} f=:fTmp; [f['a', y] for x,y in sorted(f)] =============================== ['b', 'd', 'a', 'b'] f=:fTmp; [f['a',f['a', y]] for x,y in sorted(f)] =============================== ['d', 'b', 'b', 'd'] f=:fTmp; [f['a',f['a',f['a', y]]] for x,y in sorted(f)] =============================== ['b', 'd', 'd', 'b']
\ 'a', 'b', 'c', 'd' a ['b', 'd', 'a', 'b'] b = a a ['d', 'b', 'b', 'd'] b = a a a ['d', 'b', 'b', 'd']
半群であることにより、演算表の一部が定まると、演算表の他の部分まで定まってしまうこと、その定まり方の広がりは事例により異なることが分かってもらえたと思います。如何でしょうか。
「実例 1:半群の粘着性の例」で作った半群は群にもなっています。でも「実例 2:半群の粘着性の弱い実例」で作ったものは群になっていません。何がこの違いをもたらすのでしょうか?
f:G x G --> Ge
f(f(x,y),z) = f(x,f(y,z)) for ∀x,y,z ∈ G # transitive for ∀a ∈ G # surjective {f(a,x) for x ∈ G} == G and {f(x,a) for x ∈ G} == G
「半群」の章で作った下の演算表で f(a,x),f(b,x),f(c,x),f(d,x) は全て surjective です。
\ 'a', 'b', 'c', 'd' a = a ['b', 'd', 'a', 'c'] b = a a ['d', 'c', 'b', 'a'] d = a a a ['c', 'a', 'd', 'b'] c =a a a a ['a', 'b', 'c', 'd']
半群がどんなものか、半群と群の違いは定義を暗記しても分るものではありません。ここで行ったような具体例での考察・検討が必須だと考えます。ご興味を持たれた肩は、自分のコンピュータ上で同様な、御自分の興味のある分野での群論の実験を行ってみてください。
群論の教科書では天下りで置換群(対称群とも言います)が出てくることが普通です。でも 置換群が重要である理由・その必然性を説明することは稀です。ここでは その稀な説明を行ってみます。
結論から言います。置換群が重要なのは、要素 N 個任意の有限群が与えられたとき、Sn(N) の部分群の中に、与えられた有限群と同型な:すなわち一対一に対応するものが存在するからです。
「半群」の章で作った下の演算表は 'a','b','c','d' の文字要素でしたが、それを 0,1,2,3 の数値で代用できます。
\ 'a', 'b', 'c', 'd' a = a ['b', 'd', 'a', 'c'] b = a a ['d', 'c', 'b', 'a'] d = a a a ['c', 'a', 'd', 'b'] c =a a a a ['a', 'b', 'c', 'd'] \ 0 , 1 , 2 , 3 0 = 0 [ 1 , 3 , 0 , 2 ] 1 = 0 0 [ 3 , 2 , 1 , 0 ] 3 = 0 0 0 [ 2 , 0 , 3 , 1 ] 2 =0 0 0 0 [ 0', 1 , 2 , 3 ]
この数値で表した演算表の横一列を置換群で表現できます。一対一に対応させられます。[ 1, 3, 0, 2] を Sn(4)(1,3,0,2) で表現できます。
もう一つ別の関数の一対一対応があります。群の演算表の横一列を関数とみなせます。['b', 'd', 'a', 'c'] を f:GxG --> G の f('a', x) for x ∈ G とみなせます。そして f('a',x), f('b',x), f('c',x), f('d',x) を 'a', 'b','c','d' と対応させられます。
f(.., x) の集合は関数の合成の意味で演算を定義できます。
f('a',x)・f('b',x) ≡ f('a', f('b',x)) == ['c', 'a', 'd', 'b'] == f('d',x)
ここで関数の合成演算が Sn(N) での積に対応することに気づけば、「要素 N 個任意の有限群が与えられたとき、Sn(N) の部分群の中に、与えられた有限群と同型な:すなわち一対一に対応するものが存在する」を理解してもらえると思います。
# 'a' ・'b' == 'd' # f('a',x)・f('b',x) ≡ f('a', f('b',x)) S4(1,3,0,2) * S4(3,2,1,0) =============================== (2, 0, 3, 1) [ 2 , 0 , 3 , 1 ] ['c', 'a', 'd', 'b'] 'd'ここで説明した内容の証明が、英語ですが、ここやここで見られます。Caley の定理と呼ぶそうです。日本語では同様な証明を見つけられませんでした。置換群表現を使った日本語の説明はありましたが、群の表現論の一環での説明であり、趣旨が違うと思います。
Caley の定理により、任意の有限群が Sn(N) の部分群を使って表現できることになります。そして Sn(N) は python sf の octn.py モジュールに用意されています。それを S4, SS4 などと別名を与えて使ってきました。ですから任意の有限群を python sf で扱えます。
任意の有限群が置換群 Sn(N) で表されるのですから、以後は Sn(N) を中心に考えて生きます。'a','b','c','d' のような冗長な表現は避けます。
群論の教科書では、群の定義の後、巡回群、剰余類、共役類、中心化群、交換子群、正規部分群と定義・証明を連ねていきます。それを python sf による具体例の検討でなぞっていきます。 全ての群の内部には巡回群が含まれています。 全ての群は、単位元以外は共通部分を持たない部分巡回群を使って、群を分割することができます。
巡回群は単純であり、群を巡回群の積として扱えれば非常に嬉いのですが、数学の神様は、そこまで単純にはしてくれていません。
任意の可換群は、巡回群の積に分解できるのですが、非可換群は、そうはいきません。なので非可換群をより単純な要素に分割して検討するためには、剰余類、共役類、中心化群、交換子群、正規部分群といった道具立てが必要となります。
有限群では、任意の要素 s に対し、そのべき乗の集合が巡回群としての部分群となります。Sn(4) での一例を下に示します。
s=S4(2,3,1,0);{s^n for n in range(len(SS4))} =============================== set([Sn(4)(1, 0, 3, 2), Sn(4)(2, 3, 1, 0), Sn(4)(3, 2, 0, 1), Sn(4)(0, 1, 2, 3)])
S4(2,3,1,0)^n を繰り返すことで四個の要素が出てきました。 下のように S4(2,3,1,0)^4 は単位現に戻ります。このべき乗の集合が群になることは自明に近いと思います。
S4(2,3,1,0)^4 =============================== (0, 1, 2, 3)
巡回群は最も単純な群であり扱いやすいものです。群を巡回群の積とみなせれば、群の検討が楽になります。実際、任意の有限可換群は巡回群の積とみなせます。
一般の非可換群は、もうすこし複雑です。単純に巡回群の積とはみなせないことのほうが普通です。でも どんな部分群でも巡回群を含んでいることに変わりはありません。でも非可換群では、その部分群全体の集合は、巡回群の積により表される部分群全体の集合よりも大きくなることが普通です。
ここでは置換群の中に含まれる巡回群を全て引っ張り出してみます。具体例として Sn(4) を使います。手計算では丸一日をかけてもできない作業だと思います。通常の人間による手計算では、計算間違いが入り込んで収支がつかなくなるだけでしょう。
Sn(4) の全ての要素 s について {s^0, s^1 .... s^23} の集合を作り、リストに溜め込んでいく単純な処理で全ての巡回群を求められます。
//@@ lstSet = [] for s in S4.setStt: setAt = {s^n for n in range(4 3 2 1)} if not setAt in lstSet: lstSet.append(setAt) #print out the gathered cyclic groups for setAt in lstSet: #for s in sorted(setAt): for s in sorted(setAt, λ x,y:int(x.m_idx[0] - y.m_idx[0])): print s print "==============" //@@@ //copy c:\#####.### temp.py /y //python sfPP.py -fs temp.py //python __tempConverted.py (0, 1, 2, 3) (1, 0, 3, 2) ============== (0, 1, 2, 3) (1, 3, 2, 0) (3, 0, 2, 1) ============== (0, 1, 2, 3) (3, 1, 2, 0) ============== (0, 1, 2, 3) (1, 2, 3, 0) (2, 3, 0, 1) (3, 0, 1, 2) ============== (0, 1, 2, 3) (1, 2, 0, 3) (2, 0, 1, 3) ============== (0, 1, 3, 2) (0, 1, 2, 3) ============== (0, 2, 1, 3) (0, 1, 2, 3) ============== (0, 1, 2, 3) (3, 2, 1, 0) ============== (0, 1, 2, 3) (1, 0, 2, 3) ============== (0, 3, 2, 1) (0, 1, 2, 3) ============== (0, 1, 2, 3) (2, 1, 3, 0) (3, 1, 0, 2) ============== (0, 1, 2, 3) (1, 0, 3, 2) (2, 3, 1, 0) (3, 2, 0, 1) ============== (0, 1, 2, 3) (1, 3, 0, 2) (2, 0, 3, 1) (3, 2, 1, 0) ============== (0, 2, 3, 1) (0, 3, 1, 2) (0, 1, 2, 3) ============== (0, 1, 2, 3) (2, 3, 0, 1) ============== (0, 1, 2, 3) (2, 1, 0, 3) ============== (0, 1, 2, 3) ==============
上では Python sf ブロック実行で愚直にコードを並べました。でも Python の set コンテナが順序に無関係で重複を避けることを利用すると、下のワンライナーで簡潔に書けます。Python に慣れた方ならば、こちらも容易に思いつくでしょう。
set([frozenset([s^k for k in range(2 3 4)]) for s in SS4]) =============================== [[[2, 1, 0, 3], [0, 1, 2, 3]], [[3, 2, 1, 0], [0, 1, 2, 3]], [[1, 0, 3, 2], [2, 3, 1, 0], [3, 2, 0, 1], [0, 1, 2, 3]], [[0, 3, 2, 1], [0, 1, 2, 3]], [[0, 1, 2, 3]], [[1, 0, 3, 2], [0, 1, 2, 3]], [[2, 3, 0, 1], [0, 1, 2, 3]], [[3, 1, 2, 0], [0, 1, 2, 3]], [[2, 0, 1, 3], [1, 2, 0, 3], [0, 1, 2, 3]], [[3, 1, 0, 2], [2, 1, 3, 0], [0, 1, 2, 3]], [[0, 1, 3, 2], [0, 1, 2, 3]], [[3, 0, 1, 2], [1, 2, 3, 0], [2, 3, 0, 1], [0, 1, 2, 3]], [[0, 2, 3, 1], [0, 3, 1, 2], [0, 1, 2, 3]], [[1, 0, 2, 3], [0, 1, 2, 3]], [[3, 2, 1, 0], [2, 0, 3, 1], [1, 3, 0, 2], [0, 1, 2, 3]], [[0, 2, 1, 3], [0, 1, 2, 3]], # 巡回群ごとの開業は、エディタ上マニュアルで行いました。
Sn(4) に含まれる任意の部分群は、上の巡回群を含みます。逆に言えば、上の巡回群の組み合わせ集合が生成する群で全ての部分群が尽くされることになります。 python -m temp.py
ならば、上の Sn(4) の巡回群を有名どころの SA4, Sn(3), 四面体群,SA4 の交換子群 で囲みながら分類してみると下のようになります。
┌──────────────┐ (0, 1, 2, 3) │┌────────┐ │ (2, 3, 1, 0) ││ (0, 1, 2, 3) │ │ (1, 0, 3, 2) ││ (1, 0, 3, 2):hy│ │ (3, 2, 0, 1) ││ ============ │ │ ============== ││ │ │ ││ │ │ (0, 1, 2, 3) ││ │ │ (1, 2, 3, 0) ││ (0, 1, 2, 3) │ │ (2, 3, 0, 1) ││ (2, 3, 0, 1):hx│ │ (3, 0, 1, 2) ││ ============ │SA4 の │SA4 ============== ││ │交換子群│ ││ │ │ (0, 1, 2, 3) ││ │ │ (1, 3, 0, 2) ││ (0, 1, 2, 3) │ │ (3, 2, 1, 0) ││ (3, 2, 1, 0):hz│ │ (2, 0, 3, 1) ││ ============= │ │ ============== │└────────┘ │ ┌─────────┘ │ │ │ │ (0, 1, 2, 3) │ │ (0, 2, 3, 1):a1←────────┐ │ │ (0, 3, 1, 2):b1←────────┤ │ │ ============== │ │ │ │ │ │ (0, 1, 2, 3) │ │ │ (1, 3, 2, 0):a3←────────┼┐ │ │ (3, 0, 2, 1):b3←────────┼┤ │ │ ============== ││ │ │ ││ │ │ (0, 1, 2, 3) ││ │ │ (2, 1, 3, 0):b1←────────┼┼┐ │ │ (3, 1, 0, 2):a2←────────┼┼┤ │ │ ============== │││ │ │ │││ │ │┌────────┬───┐ │││ │ ││ (0, 1, 2, 3) │ │ │││ │ ││ (1, 2, 0, 3):b4│SA3 │ │││ │ ││ (2, 0, 1, 3):a4│ │ │││ │ ││ ============== │ │ │││ │ └┼────────┴───┼────┼┼┼───┘ │ │ │││ │ (0, 1, 2, 3) │ │││ │ (0, 2, 1, 3) │Sn(3) │││ │ ============== │ │││ ├────────┐ │ │││ │ (0, 1, 2, 3) │ │ │││ │ (1, 0, 2, 3) │Sn(2) │ │││ │ ============== │ │ │││ ├────────┘ │ │││ │ (0, 1, 2, 3) │ │││ │ (2, 1, 0, 3) │ │││ │ ============== │ │││ └────────────┘ │││ │││ (0, 1, 2, 3) │││ (0, 1, 3, 2) ─┐────┐ │││ ============ │ │ │││ *├────┼───┘││ (0, 1, 2, 3) │ │ ││ (0, 3, 2, 1) ─┘─┐ *├────┼┘ ============== │ │ │ *├──┼────┘ (0, 1, 2, 3) │ │ (3, 1, 2, 0) ───┘──┘ ============== (0, 1, 2, 3) ============== a1, a2, a3, a4, b1, b2, b3, b4, hx, hy, hz は四面体群
上の図が何か綺麗に感じられたため、Sn(4) の部分群は上のものだけで尽くされたかもと私は思ってしまいました。でも、下のように S4(3,0,2,1), S4(0,3,2,1), S4(0,1,2,3) 三つの要素がを含む最小の部分群を作ってみると、上の図にはない、位数が 6 の部分群ができてしまいます。
fXY=λ setAg: frozenset([x y for x,y in mitr(setAg,setAg)]);group = λ setAg:setAg if fXY(setAg) == setAg else group(fXY(setAg));group({S4(3,0,2,1),S4(0,3,2,1), S4(0,1,2,3)}) =============================== frozenset([Sn(4)(3, 1, 2, 0), Sn(4)(1, 0, 2, 3), Sn(4)(1, 3, 2, 0), Sn(4)(0, 3, 2, 1), Sn(4)(0, 1, 2, 3), Sn(4)(3, 0, 2, 1)])
ちなみに上の fXY=λ setAg: frozenset([x y for x,y in mitr(setAg,setAg)]) は、群要素からなる集合 setAg が与えられたとき、その二つの組み合わせによる積の全部が作る集合を返しています。setAg が部分集合であることは、fXY(setAg) == setAg であることと同値です。そうなるまでリカーシブに fXY(setAg) 呼び出しを繰り返しすことで、setAg が生成する最小の部分群を生成させる作業を、group(setAg) は行います。
to be continued
群の定義は「単位元・逆元を持つ半群」で成されることが普通です。でも次のような定義をなされることも、よくあります。
群の定義 1 半群 f G x G --> G が次の性質を満たすとき, (G, f) は群 group であるという。 for ∀a∀b ∈G (∃x∃y ∈ G, f(a,x) = b and f(y,a) = b )
この定義の方が単位元・逆元に言及しないので、より基本的な定義になっていると思います。そして この「群の定義 1」と、下の「群の定義 2」が必要十分条件の関係にあることは自明に近いと思います。
群の定義 2 半群 f G x G --> G が次の性質を満たすとき, (G, f) は群 group であるという。 for ∀a∀b ∈ G # surjective {f(a,x) for y ∈ G } == G and {f(a,x) for y ∈ G } == G
でも この「群の定義 2」の方が すなわち「f:GxG --> G が 推移的:transitive であり、同時に全射:surjective である」ことの方が、より根源的な群の説明になっていると私は思います。単位元/逆元とは無関係な transitive/surjective の二つだけから単位元・逆元の存在が導かれるのですから。私は、この群の定義により、抽象群をやっと理解できた気になれました。皆様は如何でしょうか。
単位元・逆元による群の定義から「群の定義 1」を導くことは簡単です。x=a^-1 b, y=b a^-1 としてやれば、transitive 性から導けます。逆は少し複雑です。以下のような具合です。
●単位元の存在の証明
○ (a0 e == a0 for ∃e∈G) for ∀a0∈G ==> その e について(a e == a ∀a∈G) proof (a0 e == a0 for ∃e∈G) for ∀a0∈G (y a0 == a for ∃y∈G) for ∀a ∈G ((a e == y a0 e == y a0= a for ∃y∈G) for ∀a ∈G) for ∀a0 ∈G ∴ (a0 e == a0 for ∃e∈G) ==> その e は (a e == a ∀a ∈G)
○ 上の e について (e a == a ∀a∈G) proof 任意に与えられた a に対して (a b == e for ∃e∈G) 上の b について (b c == e for ∃c∈G) ∴ a == a e == a (b c) == (a b) c == e c ee = e だから a == e c == (e e) c == e a ○ e は唯一つである e の他に f も単位元だとする。 (e a == a e == a for ∀a∈G) for ∃e∈G) ---- (1) (f a == a f == a for ∀a∈G) for ∃f∈G) ---- (2) (1) において a = f とすれば f e == e f == f (1) において a = e とすれば e f == f e == e ∴ f == e
●右逆元は左逆元でもあり、また その逆元が唯一つである
(a a_1 == e for ∃a_1∈G) for ∀a ∈G ○上の a, a_1 に対して a_1 a == e である proof (a_1 c == e for ∃c∈G) この c について c = ec = (a a_1) c = a (a_1 c) = a e = a ∴ e == a_1 c == a_1 a
○ a の逆元は唯一つである proof a d == a a_1 == e のとき d == a_1 を証明する d == e d == (a_1 a) d == a_1 (a d) == a_1 e == a_1