特定のサイズのグループをカウントする


21

グループ

抽象代数では、グループはタプル(G,)であり、ここでGは集合であり、は次のような関数G×GGです。

  • すべてのためのx,y,zにおけるG(xy)z=x(yz)

  • 要素が存在するにおけるGような、そのすべてのためのxにおけるGX * E = XeGxGxe=x

  • におけるG、要素が存在するYにおけるGように、X * Y = eはxGyGxy=e

グループの順序は、Gの要素の数として定義されます。GG

厳密に正の整数ごとに、次数nのグループが少なくとも1つ存在します。例えば、C N+ Nはそのような基であり、C N = { 0 n 1 }およびx + n y = x + y nnCn+nCn={0n1}バツ+ny=バツ+ymodn

同型群

LET と定義することによりG:={12}x y = x × y バツy=バツ×ymod3。次に、および1 2 = 2 = 2 1です。11=1=2212=2=21

同様に、および0 + 2 1 = 1 = 1 + 2 0です。0+20=0=1+210+21=1=1+20

グループC 2+ 2の要素と操作は異なる名前を持っていますが、グループは同じ構造を共有しています。GC2+2

二つのグループ及びG 2* 2であると言われている同型全単射が存在する場合、φ G 1G 2ようにφ X * 1、Y = φ X * 2 φ Y すべてのためのx yのG 1G11G22ϕG1G2ϕバツ1y=ϕバツ2ϕyバツyG1

同じ順序のすべてのグループが同型というわけではありません。たとえば、クライングループ同型ではない次数4のグループです。C4+4

仕事

入力として負でない整数nを受け入れ、次数nの非同型グループの数を出力または返すプログラムまたは関数を作成します。

テストケース

Input   Output
0       0
1       1
2       1
3       1
4       2
5       1
6       2
7       1
8       5
9       2
10      2
11      1
12      5
13      1
14      2
15      1
16      14
17      1
18      5
19      1
20      5

OEIS A000001から取得)

追加のルール

  • 実行時間やメモリ使用量に制限はありません。

  • Mathematicaのようなこのタスクを単純化するビルトインFiniteGroupCountは許可されていません。

  • 標準の規則が適用されます。


14
もちろん、 Mathematicaにはこれが組み込まれています。:/
アレックスA.

1
Peterの引用(Evolution of OEISのサンドボックスポストへのコメントから):「A000001、A000003、A000019 などの「式」および「プログラム」セクションを見ると、特殊なビルトインを使用しない回答には、多くの研究。」(エンファシス鉱山。);)
マーティン・エンダー

12
Mathematicaにはない組み込みの組み込み関数はないと言う人もますが、これはまだ研究の対象です。他の神話では、Mathematicaはプログラマの心を読むことで組み込み関数を作成すると言いますが、これもまだ確認されていません。
-flawr

1
@flawr文書化されていないビルトインは、文書化されたビルトインでmonkeys_on_typewritersカバーされていないすべてをカバーします。
レベルリバーセント

(1 + 1)%3が2ではないのはなぜですか?
Cabbie407

回答:


16

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を始めました)。だから、間違いをしたり、明白なことを述べたり、恐ろしく非効率的な方法で物事を行ったりする場合は、私と一緒に耐えてください。

私のアプローチは強引ですが、もう少し賢くしようとしました。主な手順は次のとおりです。

  1. 次数nのグループに対してすべての可能なオペランドを生成します(つまり、次数nのすべてのグループを列挙します)。
  2. 次数nの 2つのグループ間で可能な全単射φを生成します。
  3. 手順1と2の結果を使用して、次数nの 2つのグループ間のすべての同型を決定します。
  4. 手順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

以上です。


2
それは説明の一つです。いいね
The_Basset_Hound

1
@The_Basset_Hound ... aaaand it it now finished;)
アンドレア

私は自分の答えは競合しないと考えているので、これを受け入れました。
デニス

4

CJam、73バイト

0ri:Re!Rm*{:Tz0=R,=[R,Te_]m!{~ff{T==}e_}/=&},{:T,e!{:PPff{T==P#}}%$}%Q|,+

上記のコードの時間の複雑さは、O(n!n

入力n = 4は、オンラインインタープリターにとってはすでに多すぎるです。

Javaインタープリターを使用して、入力十分なRAMと忍耐力があれば n = 5が可能です。

グループを見つける

次数nのグループ(G、∗)が与えられると、φ(e)= 0になるような任意の全単射φ:G-> C nを選ぶことができます

φは、* 'x ∗' y =φ(φ -1(x)∗φ -1(y))で定義すると、(G、∗)および(C n、∗ ')の同型になります

これは、0がニュートラル要素であるように、C nのすべてのグループ演算子を調べることで十分であることを意味します。

C nのグループ演算子∗をT [x] [y] = x ∗ yのような次元n×nの長方形配列Tで表します。

このような配列を生成するには、n行ごとにC nの順列を選択することから始めます。

このように、0はすべての(ただし、必ずしもすべての列ではありません)に存在します。つまり、eが何であれ、3番目の条件(逆行列の存在)が満たされます。

Tの最初のC nに等しいことを要求することにより、e = 0を修正できます。特に、2番目の条件(ニュートラル要素の存在)が保持されます。

Tがグループ演算子に対応することを確認するには、最初の条件(結合性)が成立することを確認するだけです。これは、C nのすべてのx、y、zに対してT [T [x] [y]] [z] == T [x] [T [y] [z]]であることをチェックすることで徹底的に行うことができます。

非同型グループのカウント

グループを見つけるための上記の方法は、いくつかの同型グループを生成します。どれが同型であるかを特定するのではなく、すべての同型グループのファミリーを生成します。

これは、すべての全単射を反復することによって達成行うことができるφ:C N - > C 、N、および関連配列決定によって定義される、Tφを[X] [Y] =φ -1(T [φ(X)] [φ(Y )])

あとは、個別のファミリの数をカウントするだけです。

コードが行うこと

0         e# Push 0. For input 0, the remaining code will crash, leaving
          e# this 0 on the stack.
ri:R      e# Read an integer from STDIN and save it in R.
e!        e# Push all permutations of [0 ... R-1].
Rm*       e# Push all arrays of 6 permutations of [0 ... R-1].
{         e# Filter; for each array:
  :T      e#   Save it in T.
  z0=R,=  e#   Check if the first column equals [0 ... R-1].
  [R,Te_] e#   Push [0 ... R-1] and a flattened T.
  m!{     e#   For both pairs (any order):
    ~     e#     Unwrap the pair.
    ff{   e#     For each X in the first: For each Y in the second:
      T== e#       Push T[X][Y].
    }     e#
  }/      e#
  =       e#   Check for equality, i.e., associativity.
  &       e#   Bitwise AND with the previous Boolean
},        e# Keep T iff the result was truthy.
{         e# For each kept array:
  :T      e#   Save it in T
  ,e!     e#   Push all permutations of [0 ... R-1].
  {       e#   For each permutation:
    :PP   e#     Save it in P. Push a copy.
    ff{   e#     For each X in P: For each Y in P:
      T== e#       Push T[X][Y].
      P#  e#       Find its index in P.
    }     e#
  }%      e#
  $       e#   Sort the results.
}%        e#
Q|,       e# Deduplicate and count.
+         e# Add the result to the 0 on the stack.

いいね 「愚かな」ブルートを試してみましたが、5に到達するのは困難だったので、スピードとバイトを交換しました。
アンドレアビオンド

1

パイソン2515の 507バイト

  • Dennisのおかげで8バイト節約されました。
def F(n):
 def f(k,*s):n==len(set(s))and S.add(s);{k and f(~-k,j,*s)for j in I}
 def c(k,*G):k and{s in G or c(~-k,s,*G)for s in S}or(I in G)&all((o(x,y)in G)&any(I==o(z,x)for z in G)for x in G for y in G)and A.add(G)
 S=set();A=S-S;I=tuple(range(n));o=lambda x,y:tuple(y[x[j]]for j in I);i=lambda G,H:any(all(o(H[B[i]],H[B[j]])==H[B[[k for k in I if G[k]==o(G[i],G[j])][0]]]for i in I for j in I)for B in S);f(n);c(n);K=list(A);[G in K and{G!=H and i(G,H)and K.remove(H)for H in K}for G in A];return len(K)

オンラインでお試しください!


順序の非同型サブグループの数の間の等価性を使用する n Σn および次数の有限グループの同型同値類の数 n

詳細バージョンへのリンク。


注文を行いますsと、G問題では?そうでない場合は、使用することができますdef f(k,*s):...f(~-k,j,*s)...def c(k,*G):...c(~-k,s,*G)....
デニス

@Dennis彼らはしません。ありがとう。
ジョナサンフレッチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.