Contando grupos de un tamaño dado

21

Grupos

En álgebra abstracta, un grupo es una tupla (sol,) , donde sol es un conjunto y es una función sol×solsol tal que se cumple lo siguiente:

  • Para todos los X,y,z en sol , (Xy)z=X(yz) .

  • Existe un elemento en G tal que para todas las x en G , x e = x .misolXsolXmi=X

  • Para cada en G , existe un elemento y en G tal que x y = e .XsolysolXy=mi

El orden de un grupo se define como el número de elementos de G .(sol,)sol

Para cada entero estrictamente positivo , existe al menos un grupo de orden n . Por ejemplo, ( C n , + n ) es un grupo tal, donde C n = { 0 , . . . , n - 1 } y x + n y = ( x + y )nortenorte(donorte,+norte)donorte={0 0,...,norte-1} .X+nortey=(X+y)modnorte

Grupos isomorfos

Deje y defina por x y = ( x × y )sol: ={1,2} . Entonces 1 1 = 1 = 2 2 y 1 2 = 2 = 2 1 .Xy=(X×y)mod311=1=2212=2=21

Del mismo modo, y 0 + 2 1 = 1 = 1 + 2 0 .0 0+20 0=0 0=1+210 0+21=1=1+20 0

Aunque los elementos y las operaciones de los grupos y ( C 2 , + 2 ) tienen nombres diferentes, los grupos comparten la misma estructura.(sol,)(do2,+2)

Se dice que dos grupos y ( G 2 , 2 ) son isomórficos si existe una biyección ϕ : G 1G 2 tal que ϕ ( x 1 y ) = ϕ ( x ) 2 ϕ ( y ) para todo x , y en G 1 .(sol1,1)(sol2,2)ϕ:sol1sol2ϕ(X1y)=ϕ(X)2ϕ(y)X,ysol1

No todos los grupos del mismo orden son isomorfos. Por ejemplo, el grupo Klein es un grupo de orden 4 que no es isomorfo a .(do4 4,+4 4)

Tarea

Escriba un programa o función que acepte un número entero no negativo n como entrada e imprima o devuelva el número de grupos no isomórficos de orden n .

Casos de prueba

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

(tomado de OEIS A000001 )

Reglas adicionales

  • No hay límites en tiempo de ejecución o uso de memoria.

  • FiniteGroupCountNo se permiten las funciones integradas que trivializan esta tarea, como las de Mathematica .

  • Aplican reglas estándar de .

Dennis
fuente
14
Por supuesto, Mathematica tiene algo incorporado para esto. : /
Alex A.
1
Citando a Peter (de un comentario en el post caja de arena de Evolución de OEIS ): "Si nos fijamos en la 'fórmula' y '' secciones de programa para, por ejemplo A000001 , A000003, A000019 entonces una respuesta que no utiliza órdenes internas especializadas necesitan una mucha investigación ". (El énfasis es mío);)
Martin Ender
12
Algunos dicen que no hay Mathematica incorporado no tiene, pero esto todavía está sujeto a investigación. Otros mitos dicen que Mathematica crea las construcciones leyendo la mente de los programadores , pero esto tampoco ha sido confirmado.
flawr
1
@flawr el archivo no documentado monkeys_on_typewriterscubre todo lo que no está cubierto por los documentos documentados.
Level River St
¿Por qué (1 + 1)% 3 no es 2?
Cabbie407

Respuestas:

16

CJam, 189 187 bytes

Esto va a ser difícil de explicar ... La complejidad del tiempo está garantizada 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=},,}{!!}?

Si eres lo suficientemente valiente, pruébalo en línea . En mi horrible computadora portátil, puedo obtener hasta 6 con el intérprete de Java o 5 en el intérprete en línea.

Explicación

No tengo una gran experiencia en matemáticas (acabo de terminar la escuela secundaria, comenzando CS en la universidad la próxima semana). Así que tengan paciencia conmigo si cometo errores, digo lo obvio o hago las cosas de maneras terriblemente ineficaces.

Mi enfoque es una fuerza bruta, aunque traté de hacerlo un poco más inteligente. Los pasos principales son:

  1. Genere todos los operandos posibles para un grupo de orden n (es decir, enumere todos los grupos de orden n );
  2. Genere todas las biyecciones posibles φ entre dos grupos de orden n ;
  3. Usando los resultados de los pasos 1 y 2, determine todos los isomorfismos entre dos grupos de orden n ;
  4. Usando el resultado del paso 3, cuente el número de grupos hasta el isomorfismo.

Antes de ver cómo se realiza cada paso, eliminemos un código trivial:

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)

El siguiente algoritmo no funciona correctamente con n <4 , los casos de 0 a 3 se manejan con una doble negación.

De ahora en adelante, los elementos de un grupo se escribirán como {1, a, b, c, ...} , donde 1 es el elemento de identidad. En la implementación de CJam, los elementos correspondientes son {0, 1, 2, 3, ...} , donde 0 es el elemento de identidad.

Comencemos por el paso 1. Escribir todos los operadores posibles para un grupo de orden n es equivalente a generar todas las tablas n × n Cayley válidas . La primera fila y la columna son triviales: ambas son {1, a, b, c, ...} (de izquierda a derecha, de arriba a abajo).

      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

Saber que una tabla de Cayley también es un cuadrado latino reducido (debido a la propiedad de cancelación) permite generar las posibles tablas fila por fila. A partir de la segunda fila (índice 1), generamos todas las permutaciones únicas para esa fila, manteniendo la primera columna fija en el valor del índice.

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

No todas esas permutaciones son válidas, por supuesto: cada fila y columna debe contener todos los elementos exactamente una vez. Se utiliza un bloque de filtro para este propósito (un valor verdadero mantiene la permutación, un valor falso lo elimina):

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

Tenga en cuenta que estoy filtrando dentro del ciclo de generación: esto hace que el código sea un poco más largo (en comparación con la generación y el filtrado distintos), pero mejora enormemente el rendimiento. El número de permutaciones de un conjunto de tamaño. n es n! , por lo que la solución más corta requeriría mucha memoria y tiempo.

Una lista de tablas de Cayley válidas es un gran paso para enumerar los operadores, pero al ser una estructura 2D, no puede verificar la asociatividad, que es una propiedad 3D. Entonces, el siguiente paso es filtrar las funciones no asociativas.

{                                 }, 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]

¡Uf! Mucho trabajo, pero ahora hemos enumerado todos los grupos de orden n (o mejor, las operaciones en él, pero el conjunto es fijo, por lo que es lo mismo). Siguiente paso: encontrar isomorfismos. Un isomorfismo es una biyección entre dos de esos grupos de modo que φ (x ∗ y) = φ (x) ∗ φ (y) . Generar esas biyecciones en CJam es trivial: Ne!lo hará. ¿Cómo podemos verificarlos? Mi solución comienza con dos copias de la tabla de Cayley para x ∗ y . En una copia, φ se aplica a todos los elementos, sin tocar el orden de las filas o columnas. Esto genera la tabla para φ (x ∗ y) . En el otro, los elementos se dejan como están, pero las filas y columnas se asignan a través de φ . Es decir, la fila / columnax se convierte en la fila / columna φ (x) . Esto genera la tabla para φ (x) ∗ φ (y) . Ahora que tenemos las dos tablas, solo tenemos que compararlas: si son iguales, hemos encontrado un isomorfismo.

Por supuesto, también necesitamos generar los pares de grupos para probar el isomorfismo. Necesitamos todas las 2 combinaciones de los grupos. Parece que CJam no tiene operador para las combinaciones. Podemos generarlos tomando cada grupo y combinándolo solo con los elementos que lo siguen en la lista. Dato curioso: el número de 2 combinaciones es n × (n - 1) / 2 , que también es la suma de los primeros n - 1 números naturales. Dichos números se llaman números triangulares: pruebe el algoritmo en papel, una fila por elemento fijo, y verá por qué.

: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

El código anterior corrige el primer elemento del par, L [X] , y lo combina con otros grupos (llamemos a cada uno de esos Y ). Pasa el par a un bloque de prueba de isomorfismo que mostraré en un momento. El bloque devuelve una lista de valores de Y para los que L [X] es isomorfo a Y . Entonces L [X] se agrega a esta lista. Antes de entender por qué las listas están configuradas de tal manera, veamos la prueba de isomorfismo:

\_@                                      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

Genial, ahora tenemos una lista de conjuntos como [{L [0], Y1, Y2, ...}, {L [1], Y1, ...}, ...] . La idea aquí es que, por propiedad transitiva, si dos conjuntos tienen al menos un elemento en común, entonces todos los grupos en los dos conjuntos son isomórficos. Se pueden agregar en un solo conjunto. Como L [X] nunca aparecerá en las combinaciones generadas por L [X + ...] , después de agregar cada conjunto de isomorfismos tendrá un elemento único. Entonces, para obtener el número de isomorfismos, es suficiente contar cuántos grupos aparecen exactamente una vez en todos los grupos de grupos isomórficos. Para hacer esto, desenvuelvo los conjuntos para que se vean como [L [0], Y1, Y2, ..., L [1], Y1, ...] , clasifico la lista para crear grupos del mismo grupo y finalmente RLE-codificarlo.

:~            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

Eso es todo amigos.

Andrea Biondo
fuente
2
Esa es una gran explicación. Agradable.
The_Basset_Hound
1
@The_Basset_Hound ... aaay ahora está terminado;)
Andrea Biondo el
Considero que mi propia respuesta no es competitiva, así que he aceptado esta.
Dennis
4

CJam, 73 bytes

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

La complejidad temporal del código anterior es peor que O (n! N ) .

La entrada n = 4 ya es demasiado para el intérprete en línea .

Usando el intérprete de Java , la entrada n = 5 podría ser posible, si tiene suficiente RAM y paciencia.

Encontrar grupos

Dado un grupo (G, ∗) de orden n , podemos elegir una biyección arbitraria φ: G -> C n tal que φ (e) = 0 .

φ se convertirá en un isomorfismo de (G, ∗) y (C n , ∗ ') si definimos ∗' por x ∗ 'y = φ (φ -1 (x) ∗ φ -1 (y)) .

Esto significa que es suficiente estudiar todos los operadores de grupo en C n de modo que 0 sea ​​el elemento neutral.

Representaremos un operador de grupo en C n por una matriz rectangular T de dimensiones n × n tal que T [x] [y] = x ∗ y .

Para generar tal matriz, podemos comenzar eligiendo una permutación de C n para cada una de sus n filas.

De esta forma, 0 estará presente en todas las filas (pero no necesariamente en todas las columnas ), lo que significa que se cumplirá la tercera condición (existencia de un inverso), sea cual sea e .

Podemos arreglar e = 0 al requerir que la primera columna de T sea ​​igual a C n . En particular, se mantendrá la segunda condición (existencia de un elemento neutral).

Para verificar que T corresponde a un operador de grupo, todo lo que queda por hacer es verificar que se cumpla la primera condición (asociatividad). Esto se puede hacer exhaustivamente comprobando que T [T [x] [y]] [z] == T [x] [T [y] [z]] para todos x, y, z en C n .

Contando grupos no isomórficos

El método anterior para encontrar grupos producirá algunos grupos isomórficos. En lugar de identificar cuáles son isomórficos, generamos la familia de todos los grupos isomórficos para cada uno de ellos.

Esto se puede lograr iterando sobre todas las biyecciones φ: C n -> C n , y determinando la matriz asociada , definida por Tφ [x] [y] = φ -1 (T [φ (x)] [φ (y )]) .

Todo lo que queda por hacer es contar el número de familias distintas.

Lo que hace el código

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.
Dennis
fuente
Agradable. Intenté con un bruto "tonto", pero fue difícil llegar a 5, así que cambié bytes por velocidad.
Andrea Biondo
1

Python 2 , 515 507 bytes

  • Ahorró ocho bytes gracias a 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)

Pruébalo en línea!


Usando la equivalencia entre el número de subgrupos de orden no isomórficos norte de  Σnorte y el número de clases de equivalencia isomorfas de grupos finitos de orden norte.

Enlace a la versión detallada .

Jonathan Frech
fuente
¿Las órdenes de sy son Gimportantes? Si no, puede usar def f(k,*s):...f(~-k,j,*s)...y def c(k,*G):...c(~-k,s,*G).....
Dennis
@ Dennis No lo hacen; Gracias.
Jonathan Frech