[HOME] | [INSTALL] | [Usage] | [API Doc] | [MEMO] | [Bibliography] | [LINK]
ここでは young の基本的な使い方を解説します。 young は Python というスクリプト言語で書かれているため、Python の知 識がある程度必要です。Python のことはまったく知らないという場合、 Python Tutorial を読むなどして、基本的な知識を得てください。
モジュールのインポートには 2 種類あります。実験のため、シェルから $ python2.4 と入力して、インタープリターを立ち上げます。
>>> import young >>> from young import * # 手を抜く
上のスタイルでモジュールをインポートした場合、関数を呼び出すときに 毎回モジュールの名前を明示的に指定する必要があります。
>>> import young >>> young.partition(5) [partition(5) の実行結果] >>> young.factorial(5) [factorial(5) の実行結果] >>> young.young(5) [young(5) の実行結果]
一方で、下のスタイルでモジュールをインポートした場合、関数の呼び出しが かなり楽になります。
>>> from young import * >>> partition(5) [partition(5) の実行結果] >>> factorial(5) [factorial(5) の実行結果] >>> young(5) [young(5) の実行結果]
young モジュールをメインで使うのなら、
>>> from young import *
の方が楽でいいと思います。
インタープリター上からだけでなく、プログラムとして実行することももちろんでき ます。 実行結果は print を使って表示します。
例として、下のようなプログラムを test.py という名前で保存します。
# -*- coding: utf-8 -*- # START of test.py # sample program of Young module from young import * print "test of Young module" print "partition(5) yields" print partition(5) print "factorial(5) yields" print factorial(5) print "young(5) yields" print young(5) # END of test.py
この test.py というプログラムを実行するにはコマンドラインから次のように して実行します。
$ python2.4 test.py
インタープリターからテストするか、プログラムを書いてそれを実行するかは場 合によって使い分けてください。
まずは、組み合わせ論にからむ基本的な計算から
>>> from young import * >>> factorial(5) # 5 の階乗 : 5 x 4 x 3 x 2 x 1 120 >>> factorial(3, 5) # 3 から 5 までを掛ける : 3 x 4 x 5 60 >>> permutation(1,2,3) # (1,2,3) の順列 [[3, 1, 2], [2, 1, 3], [1, 3, 2], [2, 3, 1], [1, 2, 3], [3, 2, 1]] >>> permutation('xyz') # 'x', 'y', 'z' の順列 [['z', 'x', 'y'], ['y', 'x', 'z'], ['x', 'z', 'y'], ['y', 'z', 'x'], ['x', 'y', 'z'], ['z', 'y', 'x']] >>> combination(4, 2) # 4個から2個を選ぶ組合せの数 6 >>> combination('python', 2) # 文字列 "python" から2文字選ぶ [['p', 'y'], ['p', 't'], ['p', 'h'], ['p', 'o'], ['p', 'n'], ['y', 't'], ['y', ' h'], ['y', 'o'], ['y', 'n'], ['t', 'h'], ['t', 'o'], ['t', 'n'], ['h', 'o'], ['h ', 'n'], ['o', 'n']]
combination の結果は第一引数が整数か sequence かによって、異なります。
カタラン数を少し。
>>> catalan(20) 6564120420L >>> for i in range(10): # 0 から 9 までのカタラン数を表示 ... print i, catalan(i) ... 0 1 1 1 2 2 3 5 4 14 5 42 6 132 7 429 8 1430 9 4862
現在対応している Stirling 数は starling1(Stirling number of the first kind | 第 1 種)、signless_stirling1(signless Stirling number of the first kind | 符号なし第 1 種)、starling2(Stirling number of the second kind | 第 2 種)です。
>>> for n in range(1, 6): ... for i in range(1, n+1): ... print stirling1(n, i), ... print ... 1 -1 1 2 -3 1 -6 11 -6 1 24 -50 35 -10 1
次に自然数 N の分割を与えます。
>>> from young import * >>> partition(5) (5) (4,1) (3,2) (3,1,1) (2,2,1) (2,1,1,1) (1,1,1,1,1) >>> p = partition(10) >>> p.size() # 10 の分割はどれだけあるのか 42 >>> for x in take(p, 5): # 10 の分割のうち、最初の 5 個を表示 ... print x ... (1,1,1,1,1,1,1,1,1,1) (2,1,1,1,1,1,1,1,1) (2,2,1,1,1,1,1,1) (2,2,2,1,1,1,1) (2,2,2,2,1,1) >>> for x in drop(p, 37): # 最後の 5 個を表示(42 - 37 = 5) ... print x ... (7,3) (8,1,1) (8,2) (9,1) (10) # Ramanujan's upper bound # 左が p(n) の実際の数、右が Ramanujan 近似 >>> for n in (10, 20, 30, 40, 50): ... print "p(%d)"%n, partition(n).size(), partition_upper_bound(n) ... p(10) 42 48 p(20) 627 692 p(30) 5604 6080 p(40) 37338 40080 p(50) 204226 217590
Partition の引数に分割を渡すことで、直接 Partition のインスタンスを作 ることもできます。 分割は list または tuple で渡してください。
>>> p = Partition((2,1,1)) # Partition([2,1,1]) is also OK. >>> Partition(2,1,1) # this style is invalid Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: tuple() takes at most 1 argument (3 given) >>> p (2,1,1) >>> p.plot() # (2,1,1) の形をした盤を表示 ** * * >>> p_conj = p.conjugate() # (2,1,1) の conjugate >>> print p_conj (3,1) >>> p_conj.plot() # (3,1) の形をした盤を表示 *** * >>> p_conj.conjugate() == p # 確認 True >>> p.up() # 分割 (2,1,1) の up operation (3,1,1) (2,2,1) (2,1,1,1) >>> p.down() # 分割 (2,1,1) の down operation (1,1,1) (2,1)
Partition の up/down の戻り値は Partition の list(SeqOfPartition クラスのインスタンス)です。
置換群の簡単な計算もできます。 [1,2,3] を [2,1,3] に移す置換の場合、(2,1,3) で表します。
>>> from young import * >>> p = permgroup(3,2,1) >>> p PermGroup([3 2 1]) >>> p.get_inv() # 転倒数 3 >>> p.get_sgn() # 符号(-1 or +1) -1 >>> p.isodd() # 奇置換か? True >>> p.iseven() # 偶置換か? False >>> p.get_inverse() # 逆元 PermGroup([3 2 1]) >>> p = permgroup(5,3,2,1,4) >>> p.to_cycle() # 巡回置換分解 Cycle([(1 4 5) (2 3)]) >>> p.get_inverse() # 逆元 PermGroup([4 3 2 5 1]) >>> p1 = permgroup(4,3,1,2) >>> p2 = permgroup(3,1,4,2) >>> p1 * p2 # 置換群の間の演算は * で行います。 PermGroup([1 4 2 3]) >>> p2 * p1 PermGroup([2 4 3 1]) >>> p3 = p1 * p2 >>> p3 PermGroup([1 4 2 3]) >>> p4 = p3.get_inverse() # 逆元 >>> p4 PermGroup([4 1 3 2]) >>> p3 * p4 # 単位元になるかチェック PermGroup([1 2 3 4]) >>> p4 * p3 # 単位元になるかチェック PermGroup([1 2 3 4]) >>> p3.to_cycle() # 巡回置換分解 Cycle([(1) (2 3 4)]) >>> p4.to_cycle() # 巡回置換分解 Cycle([(1) (2 3 4)]) >>>
対称群のごく簡単な計算もできます。
>>> from young import * >>> s = symmgroup(4) # 4 次の対称群 >>> s.get_degree() # 次数を取得 4 >>> s.get_order() # 位数 24 >>> s.get_even() # 偶置換全体 [PermGroup([1 2 3 4]), PermGroup([1 3 4 2]), PermGroup([1 4 2 3]), PermGroup([2 1 4 3]), PermGroup([2 3 1 4]), PermGroup([2 4 3 1]), PermGroup([3 1 2 4]), PermGroup([3 2 4 1]), PermGroup([3 4 1 2]), PermGroup([4 1 3 2]), PermGroup([4 2 1 3]), PermGroup([4 3 2 1])] >>> s.get_odd() # 奇置換全体 (snip)
symmgroup(4) のかわりに SymmetricGroup(4) としても同じことです。
既約表現についても、ごく初歩的なことについてはわかります。 既約表現の同値類の完全代表系の取得、および、その次元を表示するには次のようにします。 メソッドの名前は長いです。:
>>> s = symmgroup(5) # 5次対称群 >>> s.get_representative_of_irreducible_representation() (1,1,1,1,1) (2,1,1,1) (2,2,1) (3,1,1) (3,2) (4,1) (5) >>> s.report_irreducible_representation() equivalence dimension square # 代表元、次元、次元の二乗 ------------------------------------- (1,1,1,1,1) 1 1 (2,1,1,1) 4 16 (2,2,1) 5 25 (3,1,1) 6 36 (3,2) 5 25 (4,1) 4 16 (5) 1 1 ------------------------------------- total 26 120
次はヤング図形です。 Young 盤を作るには、大きく分けて
の 3 通りあります。
young(N) とすれば、N の分割を元にしたすべての Young 標準盤が生成されます。
>>> from young import * >>> y = young(3) >>> print y # 3 の分割からなっている Young 盤をすべて表示 (3) 1 2 3 (2,1) 1 2 3 1 3 2 (1,1,1) 1 2 3
young の引数に分割を直接与えることで、その分割を基にした Young 盤が生 成されます。
>>> y = young((2,1,1)) # 分割 (2,1,1) を与える >>> print y # 分割が (2,1,1) の Young 盤 (2,1,1) 1 2 3 4 1 3 2 4 1 4 2 3
Young 盤を直接指定することもできます。 指定する Young 盤は sequence of sequence(sequence は list, tuple など) である必要があります。 この場合、すべて小文字の young ではなく、大文字で始まる Young です。
>>> y = Young([[1,2],[2],[3]]) # list of list であらわされた Young 盤を引数に渡す >>> print y 1 2 2 3
実際にヤング図形を操作してみます。
>>> y = young(4) >>> y (4) 1 2 3 4 (3,1) 1 2 3 4 1 2 4 3 1 3 4 2 (2,2) 1 2 3 4 1 3 2 4 (2,1,1) 1 2 3 4 1 3 2 4 1 4 2 3 (1,1,1,1) 1 2 3 4 >>> y.report() # 各分割に対して、標準盤がどれだけあるのか partition number square ---------------------------------------- (4) 1 1 (3,1) 3 9 (2,2) 2 4 (2,1,1) 3 9 (1,1,1,1) 1 1 ---------------------------------------- total 10 24 >>> factorial(4) # チェック 24 >>> young((3,1,1)) # 分割が (3,1,1) の標準盤 (3, 1, 1) 1 2 3 4 5 1 2 4 3 5 1 2 5 3 4 1 3 4 2 5 1 3 5 2 4 1 4 5 2 3 >>> y = young(6) >>> y.size() # 6 の分割を元にした標準盤がどれだけあるか 76 >>> for x in take(y, 4): # 6 を元にした標準盤から4個を表示 print x 1 2 3 4 5 6 1 2 4 3 5 6 1 2 5 3 4 6 1 3 4 2 5 6
次のようにして、n の分割を元にした標準盤の総数を直接求めることもできま す。ただし、この場合、どのような分割に対して、どのように数字が numbering されているかといった情報はまったくわかりません。一方で、n が ある程度大きい場合も、高速に求められるという利点があります。 young(n).size() のみについて知りたい場合の高速なアプローチが、 get_number_of_tableaux(n) です。
>>> from young import * >>> for i in range(1, 11): ... print i, get_number_of_tableaux(i) ... 1 1 2 2 3 4 4 10 5 26 6 76 7 232 8 764 9 2620 10 9496
この関数は(memoizeを使いつつ)再帰的に定義されているため、大きな数を引数 にして呼び出す際には呼び出し方に一工夫必要です。何も考えずに get_number_of_tableaux(1000) などとやると
>>> get_number_of_tableaux(1000) Traceback (most recent call last): ... long lines of traceback messages go here ... RuntimeError: maximum recursion depth exceeded
となって、ランタイムエラーになります。これを回避するためには for ループなどを使って適度に memoize させることにより、再帰が深くなり過ぎ ないようにする必要があります。
for i in range(1, 10001): n = get_number_of_tableaux(i) else: print n # show the value of get_number_of_tableaux(10000)
というように各駅停車で計算していけば、10000 のような大きな数に対しても、 確実に計算ができます。
ちなみに get_number_of_tableaux(100) は
24053347438333478953622433243028232812964119825419485684849162710512551427284402176
です(83 桁)。 1000 の場合は 1297 桁、 10000 の場合は 17872 桁あります。
>>> y = young((2,1)) # 最初に分割を与える >>> y.get_partition() # Young 盤の元になっている分割をかえす (2, 1) >>> print y (2,1) 1 2 3 1 3 2 >>> y.up() # 分割 (2,1) に up operation をする >>> y.get_partition() (2,1,1) (2,2) (3,1) >>> print y (3,1) 1 2 3 4 1 2 4 3 1 3 4 2 (2,2) 1 2 3 4 1 3 2 4 (2,1,1) 1 2 3 4 1 3 2 4 1 4 2 3 >>> for i in range(3): # さらに3回 up operation を繰り返す ... y.up() ... y.get_partition() ... print ... (2,1,1,1) (2,2,1) (3,1,1) (3,2) (4,1) (2,1,1,1,1) (2,2,1,1) (2,2,2) (3,1,1,1) (3,2,1) (3,3) (4,1,1) (4,2) (5,1) ...(snip) >>> y = young((2,1,1)) # まず分割を与える >>> y.get_partition() # チェック (2, 1, 1) >>> for i in range(4): # 今度は down operation ... y.down() ... y.get_partition() ... print ... (1,1,1) (2,1) (1,1) (2) (1) ()
Young 盤の bumping も出来ます。
>>> y = Young([[1,2,3]]) >>> print y 1 2 3 >>> y.bump(2) # bump >>> print y 1 2 2 3 >>> y1 = Young([[1,1,2],[2,2,3],[4,5]]) >>> y2 = Young([[1,1],[2]]) >>> y1 1 1 2 2 2 3 4 5 >>> y2 1 1 2 >>> y1.bump(y2) # y1 * y2 とすることも可能 >>> print y1 1 1 1 1 2 2 2 2 3 5 4 >>> y1 = Young([[1,1,2],[2,2,3],[4,5]]) >>> print y1 1 1 2 2 2 3 4 5 >>> for box in (1,1,2): ... y1.bump(box) ... print "y1 <- %d"%box ... print y1 ... y1 <- 1 1 1 1 2 2 2 3 5 4 y1 <- 1 1 1 1 1 2 2 2 3 5 4 y1 <- 2 1 1 1 1 2 2 2 2 3 5 4
skew tableaux とそれに付随する sliding, rectification, jeu de taquin を使ってみましょう。
2 つの Young 盤 T, U
T = 1 1 2 2 2 3 4 5 U = 1 1 2
があったとします。この skew tableau S = T/U は次のようになります。
>>> from young import * >>> T = Young([[1,1,2],[2,2,3],[4,5]]) >>> U = Young([[1,1],[2]]) >>> T 1 1 2 2 2 3 4 5 >>> U 1 1 2 >>> S = T / U # use / to create a skew tableau >>> S * * 2 * 2 3 4 5 >>> S.taquin() # jeu de taquin 2 2 3 5 4
skew tableau S にある inner corners を slide していくと、上のような標 準盤になります。 taquin のかわりに S.rectify() あるいはS.jeu_de_taquin() とすることもで きます。
>>> y1 = S.taquin() >>> y1.__class__.__name__ 'Young' >>> print y1 2 2 3 5 4 >>> y2 =Young([[2,2],[3,5],[4]]) >>> print y2 2 2 3 5 4 >>> y1 == y2 True
jeu de taquin の結果、 Young クラスのインスタンスが返されます。上の場 合だと、S.taquin() は Young([[2,2],[3,5],[4]]) と同じです。
Fulton P.15 の Exercise 1 を実際に解いて見ましょう。2つのYoung 盤 T, U
T = 1 2 2 3 2 3 5 5 4 4 6 5 6 U = 1 3 2
があったとします。このヤング盤の積 T * U を求めるには次のようにします。
>>> T = Young([[1,2,2,3],[2,3,5,5],[4,4,6],[5,6]]) >>> U = Young([[1,3],[2]]) >>> T 1 2 2 3 2 3 5 5 4 4 6 5 6 >>> U 1 3 2 >>> T * U # product of T and U >>> T 1 1 2 2 3 2 2 3 5 3 4 5 4 6 6 5
これを skew tableau を使って解くには、まず、次のような skew tableau を 用意します。
>>> y1 = Young([[1,1,1,1,1,3],[1,1,1,1,2],[1,2,2,3],[2,3,5,5],[4,4,6],[5,6]]) >>> y2 = Young([[1,1,1,1],[1,1,1,1]]) >>> S = y1 / y2 >>> S * * * * 1 3 * * * * 2 1 2 2 3 2 3 5 5 4 4 6 5 6
remove される部分は適当な数値(たとえば 1)で埋めておいて、y1 に contain される小さな tableau y2 を構成し、 y1/y2 で skew tableau を無理やり作 ります(少し面倒ですが)。 あとは、これに sliding を繰り返せば、上でやった T * U と同じ結果が得ら れるはずです。
>>> S.rectify() 1 1 2 2 3 2 2 3 5 3 4 5 4 6 6 5
RSK 対応の応用の 1 つである hook length formula の例。
λ = (6,5,5,3) の shape をもった標準盤の数を hook length を使って求めたい場合、次のようにします。
>>> from young import * >>> shape = (6,5,5,3) >>> h = Hook(shape) >>> h # hook length を表示 9 8 7 5 4 1 7 6 5 3 2 6 5 4 2 1 3 2 1 >>> h.number() # number メソッドで hook length formula を計算します。 6651216L
これを hook を使わずに young だけでやるには、ある shape をもった標準盤 を 実際にすべて構成し 、その数を数える必要があります。 したがって、数を知りたいだけだとしたら、極めて非効率的なことをやってい ます。具体的には次のようになります。(λのサイズはかなり小さくしてあり ます)
>>> shape = (3,2,1,1) >>> y = young(shape) # この段階で、(3,2,2,1) の標準盤をすべて構成してしまう >>> y.size() 35 >>> h = Hook(shape) # 念のために Hook を使っても計算 >>> h 6 3 1 4 1 2 1 >>> h.number() 35 >>> print y # 35 個ある分割が (3,2,1,1) の Young 標準盤を実際に表示する (3,2,1,1) 1 2 3 4 5 6 7 1 2 3 4 6 5 7 1 2 3 4 7 5 6 ... [snip]
ある shape をもった標準盤の数を求めたい場合、Hook(shape).number() が young(shape).size() よりはるかに高速です。
標準盤とは限らない(ラベリングされた tableau で各数字が必ず1度というわけではない) Young 盤 を考えます。例としては、次のような盤です。
1 2 2 3 3 5 2 3 5 5 4 4 6 6 5 6
λ = (6,4,4,2) でラベリングされる数字は [6] = {1,2,3,4,5,6} から選ばれ るとします。この条件を満たす Young 盤の数は次のようにして求めます。 (Fulton P.2 左上)
>>> from young import * >>> shape = (6,4,4,2) >>> h = Hook(shape) >>> h # display hook length 9 8 7 5 4 1 7 6 5 3 2 6 5 4 2 1 3 2 1 >>> h.number(6) # number メソッドに引数を与える。 90552L
λ = (6,5,5,3) でエントリーが [5] = {1,2,3,4,5} の場合、次のようになり ます。(Fulton P.55 右下)
>>> from young import * >>> shape = (6,5,5,3) >>> h = Hook(shape) >>> h # display hook length 9 8 6 5 2 1 6 5 3 2 5 4 2 1 2 1 >>> h.number(5) # number メソッドに引数を与える。 3360L
Stanley によるこの hook length formula は、 Weyl の指標公式の特殊な形 でもあります(と本には書いてあります。自信なし)。
Parition との連携もできます(Partition.get_hook() <-> Hook.get_partition())
>>> p1 = Partition((6,5,5,3)) # Partition のインスタンスを作成 >>> p1 (6,5,5,3) >>> h = p1.get_hook() # Partition を Hook に変換 >>> h # display hook length 9 8 7 5 4 1 7 6 5 3 2 6 5 4 2 1 3 2 1 >>> p2 = h.get_partition() # Hook を Partition に変換 >>> p2 (6,5,5,3) >>> p1 == p2 True
Robinson/Robinson-Schensted/Robinson-Schensted-Knuth 対応について。
置換を基にして同じ形(shape)の Young 標準盤のペア (P, Q) を set up しま す。 たとえば、置換 σ が
1 2 3 4 5 6 4 5 1 3 6 2
の場合 --- 順列 (4 5 1 3 6 2) ---、次のようにします。(堀田 P. 118)
>>> from young import * >>> perm = (4,5,1,3,6,2) # permutation >>> rsk = RSK() >>> rsk.set_array(perm) >>> rsk.setup() >>> rsk P 1 2 6 3 5 4 Q 1 2 5 3 4 6 # P, Q を個別に表示 >>> rsk.P 1 2 6 3 5 4 >>> rsk.Q 1 2 5 3 4 6
必要な手順は、
P は canonical procedure で構成された Young tableau で、Q は recording(insersion) tableau です。
同様にして、順列が (5 3 4 7 1 2 6) の場合、次のようにします。(堀田 P. 120 練習)
>>> perm = (5,3,4,7,1,2,6) >>> rsk = RSK() >>> rsk.set_array(perm) >>> rsk.setup() >>> rsk P 1 2 6 3 4 7 5 Q 1 3 4 2 6 7 5
長さが r で各文字が [n] = {1,2, ..., n} からとられる Word を基にして、 同じ形(shape)の Young 盤のペア (P, Q) を set up します。 たとえば、Word w = 5 4 8 2 3 4 1 7 5 3 1 の場合、次のような 2 行の列が できます。(Fulton P. 36)
1 2 3 4 5 6 7 8 9 10 11 5 4 8 2 3 4 1 7 5 3 1
これを基にして、P, Q を set up するには、次のようにします。
>>> w = (5,4,8,2,3,4,1,7,5,3,1) >>> rsk = RSK() >>> rsk.set_array(w) >>> rsk.setup() >>> rsk P 1 1 3 5 2 3 4 4 5 7 8 Q 1 3 6 8 2 5 4 9 7 10 11
この例の set_array メソッドのところで、
>>> top = (1,2,3,4,5,6,7,8,9,10,11) >>> bottom = (5,4,8,2,3,4,1,7,5, 3, 1) >>> rsk = RSK() >>> rsk.set_array(top, bottom) >>> rsk.setup()
というように明示的に two-rowed array を指定することもできます。
r = n で、Word の各文字が [n] から一度しか現れない特別な場合が Robinson 対応です。
two-rowed array で上の行が increasing order ではなく、weakly increasing order の場合、例えば、
1 1 1 2 2 3 3 3 3 1 2 2 1 2 1 1 1 2
の場合、次のようにします。(Fulton P. 40 Exercise 1)
>>> top = (1,1,1,2,2,3,3,3,3) >>> bottom = (1,2,2,1,2,1,1,1,2) >>> rsk = RSK() >>> rsk.set_array(top, bottom) # rsk.set_array((top, bottom)) は駄目。 >>> rsk.setup() >>> rsk P 1 1 1 1 1 2 2 2 2 Q 1 1 1 2 3 3 2 3 3
Symmetry Theorem(Fulton P.40) とは two-rowed array の上下を入れ替えた ら、tableaux のペア (P, Q) が (Q, P) になるというものです。 Fulton の Exercise 2(P.41) は次のようになります。
>>> from young import * >>> top = (1,1,1,2,2,3,3,3,3) >>> bottom = (1,2,2,1,2,1,1,1,2) >>> rsk = RSK() >>> rsk.set_array(bottom, top) # swap two-rowed array >>> rsk.setup() >>> rsk P 1 1 1 2 3 3 2 3 3 Q 1 1 1 1 1 2 2 2 2
top と bottom を入れ替えることで、 P. 40 の Exercise 1 とは P, Q が逆に なっています。また、set_array メソッドに渡す two-rowed array は lexicographic order になっていなくても OK です(two-rowed array が Word であるためには、lexicographic order でなければいけない)。
top と bottom を逆にして lexicographic order にソートし(top = (1,1,1,1,1,2,2,2,2), bottom = (1,2,3,3,3,1,1,2,3) となる)、 rsk.set_array(top, bottom) としても、もちろん上と同じ結果になります。
>>> from young import * >>> top = (1,1,1,1,1,2,2,2,2) >>> bottom = (1,2,3,3,3,1,1,2,3) >>> rsk = RSK() >>> rsk.set_array(top, bottom) >>> rsk.setup() >>> rsk P 1 1 1 2 3 3 2 3 3 Q 1 1 1 1 1 2 2 2 2
two-rowed array が置換の場合、top と bottom で array を入れ替えること は、ある置換群 g に対して、その逆元 g インバースを構成していることに等 しいです。 順列が (4 5 1 3 6 2) の場合を例にとると、次のようになります (順列の例は 堀田 P. 120 練習 から)
>>> from young import * >>> grp1 = PermGroup((3,2,6,1,4,5)) >>> grp2 = grp1.get_inverse() # inverse of grp1 >>> grp1, grp2 (PermGroup([3 2 6 1 4 5]), PermGroup([4 2 1 5 6 3])) >>> r1 = RSK() >>> r1.set_array(grp1) >>> r1.setup() >>> r1 P 1 4 5 2 6 3 Q 1 3 6 2 5 4 >>> r2 = RSK() >>> r2.set_array(grp2) >>> r2.setup() >>> r2 P 1 3 6 2 5 4 Q 1 4 5 2 6 3
r1 と r2 とで (P, Q) のペアが逆になっています。
R(-S(-K)) 対応で使われる array には行列表示があります(Fulton P. 41)。 この行列は、array の top/bottom を入れ替えることで、もとの行列の転置行 列になります。行列を得るには、 RSK クラスの get_matrix メソッドを使い ます。上の r1, r2 をそのまま使うと
>>> from pprint import pprint # 標準ライブラリに付属の pretty printer >>> pprint(r1.get_matrix()) # RSK クラスの get_matrix メソッドを使う [[0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0]] >>> pprint(r2.get_matrix()) [[0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0]]
この2つの行列は確かに互いに転置行列になっています。
Fulton 4.2 節の Matrix-ball について。(まだ部分的)
ボールのナンバリングには Numbering クラスを使います。(例は Fulton P. 43 から)
>>> from young import * >>> A = [[1,2], [1,1], [3,1]] >>> ball = Numbering(A) >>> print ball # numbering of matrix A 1 | 2 3 ------------------------- 2 | 4 ------------------------- 3 4 5 | 6 -------------------------
Numbering インスタンスの constructP(あるいは constructQ)を n 回呼び出 すごとに、それが n 行目の P(あるいは Q) になります。
>>> ball.constructP() # first row of P [1, 1, 1, 1, 1, 2] >>> ball.constructQ() # first row of Q [1, 1, 1, 2, 3, 3]
同じラベルのボールがなくなるまで(= Numbering.has_same_lable() が False を返すまで)このプロセス(ナンバリング)を繰り返していきます。上の ball では 2,3,4 のラベルを持ったボールがそれぞれ2つずつあります。
>>> ball.get_derived_matrix() # ``derived'' matrix を取得 [[0, 0], [0, 1], [0, 2]] >>> m = ball.get_derived_matrix() >>> ball2 = Numbering(m) >>> print ball2 | ------------------------- | 1 ------------------------- | 2 3 ------------------------- >>> ball2.constructP() # second row of P [2, 2, 2] >>> ball2.constructQ() # second row of Q [2, 3, 3] >>> ball2.has_same_label() # ball2 にある 1,2,3 のラベルを貼られたボールはそれぞれ一個しかない。 False >>> ball2.get_derived_matrix() [[0, 0], [0, 0], [0, 0]] >>> ball3 = Numbering(ball2.get_derived_matrix()) >>> print ball3 | ------------------------- | ------------------------- | -------------------------
上の結果から、P の1行目は [1, 1, 1, 1, 1, 2]、 2行目は [2, 2, 2] 3行目以 降はなし。同様に Q の1行目は [1, 1, 1, 2, 3, 3]、2行目は [2, 3, 3] 3行目 以降はなし、となります。
# P [[1, 1, 1, 1, 1, 2], [2, 2, 2]] # Q [[1, 1, 1, 2, 3, 3], [2, 3, 3]]
P, Q が構成される過程を確認したい場合は便利です が、単に P, Q を知りたいだけだと、少し面倒です。
行列を基にして、対応する Young 図形のペアを取得するには MatrixBall クラ スを使います。
>>> from young import * >>> A = [[1,2], [1,1], [3,1]] >>> mb = MatrixBall(A) >>> print mb # tableau P 1 1 1 1 1 2 2 2 2 # tableau Q 1 1 1 2 3 3 2 3 3 >>> print mb.P # P, Q を個別に表示 1 1 1 1 1 2 2 2 2 >>> print mb.Q 1 1 1 2 3 3 2 3 3
P, Q は Numbering から求めたものと同じになっています。
対称行列 A に対しては、
行列 A が対称行列 <==> P = Q
という性質を満足します(P. 46)。 Exercise 4(P. 47) とあわせて確認します。
>>> A = [[1,2,1], [2,2,0], [1,0,1]] # symmetric matrix >>> mb = MatrixBall(A) >>> print mb # P = Q であることを確認 # tableau P 1 1 1 1 2 3 2 2 2 3 # tableau Q 1 1 1 1 2 3 2 2 2 3 >>> ball = Numbering(A) >>> A_flat = ball.get_derived_matrix() >>> print A [[1, 2, 1], [2, 2, 0], [1, 0, 1]] >>> print A_flat [[0, 0, 0], [0, 2, 1], [0, 1, 0]] >>> trace(A) # 行列のトレースを計算するには、trace 関数を使う 4 >>> trace(A_flat) 2 >>> len(mb.P[0]) # length of first row of P 6 # Check Exercise 4 >>> trace(A) + trace(A_flat) == len(mb.P[0]) True
別の対称行列を使って、同じことを確認します。
>>> A2 = [[4,1],[1,0]] # symmetric matrix >>> mb = MatrixBall(A2) >>> ball = Numbering(A2) >>> A2_flat = ball.get_derived_matrix() >>> print mb # P = Q であることを確認 # tableau P 1 1 1 1 1 2 # tableau Q 1 1 1 1 1 2 >>> A2 [[4, 1], [1, 0]] >>> A2_flat [[0, 0], [0, 1]] >>> trace(A2_flat) 1 >>> trace(A2) 4 >>> len(mb.P[0]) # length of first row of P 5 # check Exercise 4 >>> trace(A2) + trace(A2_flat) == len(mb.P[0]) True
対称行列に負の整数値がある場合は、当然ながらうまくいきません。
Fulton P.45-46 のややこしいアルゴリズムは、MatrixBall.get_tableau_pair メソッドでちょこっと利用しています。
tableau のペアから行列を復元することなどがまだ実装できていません。
to be continued.