计算给定大小的组


21

团体

在抽象代数中,一个是一个元组,其中是一个集合,而是一个函数,使得以下内容成立:G G × G G(G,)GG×GG

  • 对于所有的在,。G x y z = x y z x,y,zG(xy)z=x(yz

  • 存在一个元件在使得对于所有中,。G x G x e = xËGXGxe=X

  • 对于每一个在,存在一个元素在使得。G y G x y = eXGÿGxy=Ë

组的顺序定义为的元素数。GGG

对于每个严格的正整数,至少存在一组阶。例如,是这样的组,其中和。Ñ ç Ñ+ ÑÇ Ñ = { 0 n 1 } x + n y = x + y ññCñ+ñCñ={0ñ-1个}X+ñÿ=X+ÿñ

同构群

令并通过x \ ast y =(x \ times y)\ mod3定义\ ast。然后1 \ ast1 = 1 = 2 \ ast21 \ ast2 = 2 = 2 \ ast1* X * Ý = X × Ý G:={1个2}1 * 1 = 1 = 2 * 2 1 * 2 = 2 = 2 * 1Xÿ=X×ÿ311=1=2212=2=21

同样,0+20=0=1+210+21=1=1+20

尽管组(G,)(C_2,+ _ 2)的元素和操作(C2,+2)具有不同的名称,但是这些组共享相同的结构。

如果存在双射\ phi:G_1 \ rightarrow G_2使得\ phi(x \ ast_1y)= \ phi(x)\ ast_2 \ 则说成两组(G1,1)(G2,2)同构的。披(Y)为所有的x,yG_1ϕ:G1G2ϕ(x1y)=ϕ(x)2ϕ(y)x,yG1

并非所有相同顺序的组都是同构的。例如,克莱因基团是与(C_4,+ _ 4)不同构的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

附加规则


14
当然, Mathematica对此具有内置功能。:/
Alex A.

1
引用彼得(从对OEIS演变的沙盒帖子的评论中得出):“如果您查看“公式”和“程序”部分,例如A000001,A000003,A000019,则不使用专用内置函数的答案将要求很多研究。” (强调);)
马丁·恩德

12
有人说没有内置的Mathematica 没有,但这仍然有待研究。其他神话说Mathematica通过阅读程序员的思想来创建内建函数,但这还没有得到证实。
瑕疵的

1
@flawr未记录的monkeys_on_typewriters内置文件涵盖了未记录的内置文件所涵盖的所有内容。
级圣河

为什么(1 + 1)%3不是2?
Cabbie407

Answers:


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阶之间的所有可能双射φ
  3. 使用步骤1和2的结果,确定两组n阶之间的所有同构;
  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表也是缩小的拉丁方(由于cancel属性),可以逐行生成可能的表。从第二行(索引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阶的所有组(或者更好的是,对n的运算-但是集合是固定的,因此是同一件事)。下一步:找到同构。同构是这两个组之间的双射,使得φ(x ∗ y)=φ(x)∗φ(y)。在CJam中生成这些双射是微不足道的:Ne!将做到这一点。我们如何检查它们?我的解决方案从凯莱表的两个副本开始X * Y。在一个副本上,将φ应用于所有元素,而不会触及行或列的顺序。这将生成φ(x ∗ y)的表。另一方面,元素保持原样,但行和列通过φ映射。也就是说,行/列x成为行/列φ(x)。这将生成φ(x)∗φ(y)的表。现在我们有了这两个表,我们只需要比较它们:如果它们相同,就可以发现同构。

当然,我们还需要生成成对的组以测试同构。我们需要组的所有2个组合。看起来CJam没有组合运算符。我们可以通过获取每个组并将其与列表中紧随其后的元素进行组合来生成它们。有趣的事实:2个组合的数量为n×(n-1)/ 2,这也是前n-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,...},...]等集合的列表。这里的想法是,根据传递性质,如果任意两个集合具有至少一个共同的元素,则这两个集合中的所有基团都是同构的。它们可以聚合为一个集合。由于L [X]永远不会出现在由L [X + ...]生成的组合中,所以在聚合每一组同构后,将具有一个唯一元素。因此,要获得同构的数量,只需计算在所有同构组集中恰好出现一次的组数即可。为此,我解开了集合,使它们看起来像[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 ... aaa,现在完成了;)
Andrea Biondo

我认为自己的答案没有竞争力,因此我已经接受了这一答案。
丹尼斯

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已经足够了。

如果您有足够的RAM和耐心,可以使用Java解释器输入n = 5

寻找团体

给定一个n阶的组(G,∗),我们可以选择一个任意双射φ:G-> C n,使得φ(e)= 0

如果我们通过x ∗'y =φ(φ -1(x)∗φ -1(y))定义∗',则φ将成为(G,∗)(C n,∗')的同构。

这意味着只要研究C n中的所有群算子,使得0是中性元素就足够了。

我们将用尺寸为n×n的矩形阵列T表示C n中的组算子,使得T [x] [y] = x ∗ y

要生成这样的数组,我们可以从为其n行中的每一个选择C n的置换开始。

这样,在所有(但不一定是所有)中都将出现0,这意味着无论e是多少,都将满足第三个条件(存在逆)。

我们可以固定E = 0通过要求该第一Ť等于Ç Ñ。特别是,第二个条件(中性元素的存在)将成立。

为了验证T是否对应于组运算符,剩下要做的就是验证第一个条件(关联性)成立。这可以详尽通过检查进行的是T [T [X] [Y] [Z] == T [X] [T [Y] [Z]]对于所有的x,y和zÇ Ñ

计算非同构组

上面找到组的方法会产生一些同构的组。我们没有识别出哪些同构,而是为它们每个生成了所有同构基团。

这可以通过完成迭代在所有双射来实现φ:C ñ - “ç Ñ,以及确定相关联的阵列,通过定义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

Python 2中515个 507字节

  • 感谢Dennis节省了八个字节。
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)

在线尝试!


ñ Σññ

链接到详细版本


s和的命令G重要吗?如果没有,您可以使用def f(k,*s):...f(~-k,j,*s)...def c(k,*G):...c(~-k,s,*G)....
丹尼斯

@丹尼斯他们不这样做;谢谢。
乔纳森·弗雷希
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.