CJam、189の 187バイト
これは説明するのが難しいでしょう...時間の複雑さは保証されていますO(scary)
。
qi:N_3>{,aN*]N({{:L;N,X)-e!{X)_@+L@@t}%{X2+<z{_fe=:(:+}%:+!},}%:+}fX{:G;N3m*{_~{G@==}:F~F\1m>~F\F=}%:*},:L,({LX=LX)>1$f{\_@a\a+Ne!\f{\:M;~{M\f=z}2*\Mff==}:|{;}|}\a+}fX]:~$e`{0=1=},,}{!!}?
勇気があれば、オンラインで試してみてください。安っぽいラップトップでは、Javaインタープリターで最大6つ、オンラインインタープリターで最大5つ取得できます。
説明
私は大きな数学のバックグラウンドを持っていません(高校を卒業したばかりで、来週ユニでCSを始めました)。だから、間違いをしたり、明白なことを述べたり、恐ろしく非効率的な方法で物事を行ったりする場合は、私と一緒に耐えてください。
私のアプローチは強引ですが、もう少し賢くしようとしました。主な手順は次のとおりです。
- 次数nのグループに対してすべての可能なオペランド∗を生成します(つまり、次数nのすべてのグループを列挙します)。
- 次数nの 2つのグループ間で可能な全単射φを生成します。
- 手順1と2の結果を使用して、次数nの 2つのグループ間のすべての同型を決定します。
- 手順3の結果を使用して、同型までのグループの数を数えます。
各ステップがどのように行われるかを見る前に、ささいなコードを邪魔にならないようにしましょう。
qi:N_ e# Get input as integer, store in N, make a copy
3>{...} ? e# If N > 3, do... (see below)
{!!} e# Else, push !!N (0 if N=0, 1 otherwise)
次のアルゴリズムは、正しく動作しません n <4では。0から3の場合は二重否定で処理されます。
今後、グループの要素は{1、a、b、c、...}として記述されます。ここで、1はアイデンティティ要素です。CJam実装では、対応する要素は{0、1、2、3、...}で、0はアイデンティティ要素です。
手順1から始めましょう。次数nのグループに可能なすべての演算子を書くことは、すべての有効なn×n Cayleyテーブルを生成することと同等です。最初の行と列は簡単です。両方とも{1、a、b、c、...}(左から右、上から下)です。
e# N is on the stack (duplicated before the if)
,a e# Generate first row [0 1 2 3 ...] and wrap it in a list
N* e# Repeat row N times (placeholders for next rows)
] e# Wrap everything in a list
e# First column will be taken care of later
Cayleyテーブルが(キャンセルプロパティのため)縮小ラテンスクエアであることを知っていると、可能なテーブルを行ごとに生成できます。2番目の行(インデックス1)から開始して、その行のすべての一意の順列を生成し、最初の列をインデックスの値に固定したままにします。
N({ }fX e# For X in [0 ... N-2]:
{ }% e# For each table in the list:
:L; e# Assign the table to L and pop it off the stack
N, e# Push [0 ... N-1]
X) e# Push X+1
- e# Remove X+1 from [0 ... N-1]
e! e# Generate all the unique permutations of this list
{ }% e# For each permutation:
X)_ e# Push two copies of X+1
@+ e# Prepend X+1 to the permutation
L@@t e# Store the permutation at index X+1 in L
{...}, e# Filter permutations (see below)
:+ e# Concatenate the generated tables to the table list
もちろん、これらのすべての順列が有効なわけではありません。各行と列には、すべての要素が一度だけ含まれている必要があります。この目的のためにフィルターブロックが使用されます(真の値は順列を保持し、偽の値はそれを削除します)。
X2+ e# Push X+2
< e# Slice the permutations to the first X+2 rows
z e# Transpose rows and columns
{ }% e# For each column:
_fe= e# Count occurences of each element
:( e# Subtract 1 from counts
:+ e# Sum counts together
:+ e# Sum counts from all columns together
! e# Negate count sum:
e# if the sum is 0 (no duplicates) the permutation is kept
e# if the sum is not zero the permutation is filtered away
生成ループ内でフィルタリングしていることに注意してください。これにより、コードが少し長くなります(個別の生成とフィルタリングに比べて)が、パフォーマンスが大幅に向上します。サイズnのセットの順列の数はn!、短いソリューションでは多くのメモリと時間が必要になります。
有効なCayleyテーブルのリストは、演算子を列挙するための素晴らしいステップですが、2D構造であるため、3Dプロパティである結合性をチェックできません。したがって、次のステップは、非連想関数を除外することです。
{ }, e# For each table, keep table if result is true:
:G; e# Store table in G, pop it off the stack
N3m* e# Generate triples [0 ... N-1]^3
{ }% e# For each triple [a b c]:
_~ e# Make a copy, unwrap top one
{ }:F e# Define function F(x,y):
G@== e# x∗y (using table G)
~F e# Push a∗(b∗c)
\1m> e# Rotate triple right by 1
~ e# Unwrap rotated triple
F\F e# Push (a∗b)∗c
= e# Push 1 if a∗(b∗c) == (a∗b)∗c (associative), 0 otherwise
:* e# Multiply all the results together
e# 1 (true) only if F was associative for every [a b c]
ふう!たくさんの作業がありますが、今度はn次のすべてのグループを列挙しました(または、それに対する操作が良いですが、セットは固定されているので、同じことです)。次のステップ:同型を見つけます。同型は、φ(x ∗ y)=φ(x)∗φ(y)であるような2つのグループ間の全単射です。CJamでこれらの全単射を生成するのは簡単ですNe!
。それを実行します。どうやって確認できますか?私のソリューションは、x ∗ yの Cayleyテーブルの2つのコピーから始まります。1つのコピーでは、行または列の順序を変更せずに、すべての要素にφが適用されます。これにより、φ(x ∗ y)のテーブルが生成されます。もう一方では、要素はそのままですが、行と列はφを介してマッピングされます。つまり、行/列xは行/列φ(x)になります。これにより、φ(x)∗φ(y)のテーブルが生成されます。2つのテーブルができたので、それらを比較する必要があります。それらが同じ場合、同型を見つけました。
もちろん、同型をテストするためにグループのペアを生成する必要もあります。グループの2つの組み合わせすべてが必要です。CJamには組み合わせの演算子がないようです。各グループを取得し、リスト内でそれに続く要素とのみ組み合わせることにより、それらを生成できます。楽しい事実:2の組み合わせの数はn×(n-1)/ 2で、これは最初のn-1の自然数の合計でもあります。このような数は三角数と呼ばれます。紙の上でアルゴリズムを試してみてください。固定要素ごとに1行、そしてその理由がわかります。
:L e# List of groups is on stack, store in L
,( e# Push len(L)-1
{ }fX e# For X in [0 ... len(L)-2]:
LX= e# Push the group L[X]
LX)> e# Push a slice of L excluding the first X+1 elements
1$ e# Push a copy of L[X]
f{...} e# Pass each [L[X] Y] combination to ... (see below)
e# The block will give back a list of Y for isomorphic groups
\a+ e# Append L[X] to the isomorphic groups
] e# Wrap everything in a list
上記のコードは、ペアの最初の要素L [X]を修正し、それを他のグループと組み合わせます(これらのYのそれぞれを呼び出しましょう)。ペアを同型テストブロックに渡します。これについては後で説明します。ブロックの値のリストバック与えるYれるL [X]に同形であるYを。次に、L [X]がこのリストに追加されます。リストがこのように設定されている理由を理解する前に、同型テストを見てみましょう。
\_@ e# Push a copy of Y
a\a+ e# L[X] Y -> [L[X] Y]
Ne! e# Generate all bijective mappings
\f{ } e# For each bijection ([L[X] Y] extra parameter):
\:M; e# Store the mapping in M, pop it off the stack
~ e# [L[X] Y] -> L[X] Y
{ }2* e# Repeat two times (on Y):
M\f= e# Map rows (or transposed columns)
z e# Transpose rows and columns
e# This generates φ(x) ∗ φ(y)
\Mff= e# Map elements of L[X], generates φ(x ∗ y)
= e# Push 1 if the tables are equal, 0 otherwise
:| e# Push 1 if at least a mapping was isomorphic, 0 otherwise
{;}| e# If no mapping was isomorphic, pop the copy of Y off the stack
これで、[{L [0]、Y1、Y2、...}、{L [1]、Y1、...}、...]のようなセットのリストができました。ここでの考え方は、推移的性質により、2つのセットが少なくとも1つの要素を共有している場合、2つのセットのすべてのグループは同型であるということです。それらは単一のセットに集約できます。L [X]はによって生成された組み合わせで表示されることはありませんL [X + ...] 、同型の各セットを集約した後、1つのユニークな要素を持っています。したがって、同型の数を取得するには、同型グループのすべてのセットで正確に1回出現するグループの数を数えるだけで十分です。これを行うには、セットを展開して[L [0]、Y1、Y2、...、L [1]、Y1、...]のようにし、リストをソートして同じグループのクラスターを作成し、最後にRLEエンコードします。
:~ e# Unwrap sets of isomorphic groups
$ e# Sort list
e` e# RLE-encode list
{ }, e# Filter RLE elements:
0= e# Get number of occurrences
1= e# Keep element if occurrences == 1
, e# Push length of filtered list
e# This is the number of groups up to isomorphism
以上です。