Conteggio di gruppi di una determinata dimensione


21

gruppi

Nell'algebra astratta, un gruppo è una tupla (G,) , dove G è un insieme e è una funzione G×GG tale che vale quanto segue:

  • Per tutti x,y,z in G , (xy)z=x(yz) .

  • Esiste un elemento in G tale che per tutti x in G , x e = x .eGxGxe=x

  • Per ogni in G , esiste un elemento y in G tale che x y = e .xGyGxy=e

L' ordine di un gruppo è definito come il numero di elementi di G .(G,)G

Per ogni numero intero strettamente positivo , esiste almeno un gruppo di ordine n . Ad esempio, ( C n , + n ) è un tale gruppo, dove C n = { 0 , . . . , n - 1 } e x + n y = ( x + y )nn(Cn,+n)Cn={0,...,n1} .x+ny=(x+y)modn

Gruppi isomorfi

Sia e definisci per x y = ( x × y )G:={1,2} . Quindi 1 1 = 1 = 2 2 e 1 2 = 2 = 2 1 .xy=(x×y)mod311=1=2212=2=21

Allo stesso modo, e 0 + 2 1 = 1 = 1 + 2 0 .0+20=0=1+210+21=1=1+20

Sebbene gli elementi e le operazioni dei gruppi e ( C 2 , + 2 ) abbiano nomi diversi, i gruppi condividono la stessa struttura.(G,)(C2,+2)

Due gruppi e ( G 2 , * 2 ) si dice che sono isomorfi se esiste una biiezione φ : G 1G 2 tale che φ ( x * 1 y ) = φ ( x ) * 2 ϕ ( y ) per tutte x , y in G 1 .(G1,1)(G2,2)ϕ:G1G2ϕ(x1y)=ϕ(x)2ϕ(y)x,yG1

Non tutti i gruppi dello stesso ordine sono isomorfi. Ad esempio, il gruppo Klein è un gruppo di ordine 4 che non è isomorfo .(C4,+4)

Compito

Scrivi un programma o una funzione che accetta un numero intero non negativo n come input e stampa o restituisce il numero di gruppi non isomorfi dell'ordine n .

Casi test

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

(tratto da OEIS A000001 )

Regole aggiuntive

  • Non ci sono limiti al tempo di esecuzione o all'utilizzo della memoria.

  • Gli incorporati che banalizzano questo compito, come quello di Mathematica FiniteGroupCount, non sono ammessi.

  • Si applicano le regole standard del .


14
Ovviamente Mathematica ha una struttura per questo. : /
Alex A.

1
Citando Peter (da un commento sul post sandbox di Evolution of OEIS ): "Se guardi le sezioni" formula "e" programma "per es. A000001 , A000003, A000019, una risposta che non utilizza builtin specializzati richiederà un molta ricerca ". (Enfasi mia.);)
Martin Ender,

12
Alcuni sostengono che Mathematica non sia integrata , ma questo è ancora oggetto di ricerca. Altri miti affermano che Mathematica crea i builtin leggendo la mente dei programmatori , ma anche questo non è stato ancora confermato.
flawr

1
@flawr il monkeys_on_typewritersbuiltin non documentato copre tutto ciò che non è coperto dai builtin documentati.
Level River St,

Perché (1 + 1)% 3 non è 2?
Cabbie407,

Risposte:


16

CJam, 189 187 byte

Questo sarà difficile da spiegare ... La complessità del tempo è garantita 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=},,}{!!}?

Se sei abbastanza coraggioso, provalo online . Sul mio maledetto laptop riesco a ottenere fino a 6 con l'interprete Java o 5 nell'interprete online.

Spiegazione

Non ho una grande preparazione in matematica (ho appena finito il liceo, ho iniziato a studiare in CS alla prossima settimana). Quindi abbi pazienza con me se commetto errori, dichiari l'ovvio o faccio le cose in modi orribilmente inefficaci.

Il mio approccio è una forza bruta, anche se ho cercato di renderlo un po 'più intelligente. I passaggi principali sono:

  1. Genera tutti i possibili operandi per un gruppo di ordine n (ovvero, enumera tutti i gruppi di ordine n );
  2. Genera tutte le possibili biiezioni φ tra due gruppi di ordine n ;
  3. Utilizzando i risultati dei passaggi 1 e 2, determinare tutti gli isomorfismi tra due gruppi di ordine n ;
  4. Utilizzando il risultato del passaggio 3, contare il numero di gruppi fino all'isomorfismo.

Prima di vedere come viene eseguito ogni passaggio, togliiamo un po 'di codice banale:

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)

Il seguente algoritmo non funziona correttamente con n <4 , i casi da 0 a 3 vengono gestiti con una doppia negazione.

D'ora in poi, gli elementi di un gruppo saranno scritti come {1, a, b, c, ...} , dove 1 è l'elemento identità. Nell'implementazione di CJam, gli elementi corrispondenti sono {0, 1, 2, 3, ...} , dove 0 è l'elemento identità.

Iniziamo dal passaggio 1. Scrivere tutti i possibili operatori per un gruppo di ordini n equivale a generare tutte le tabelle n × n Cayley valide . La prima riga e colonna sono banali: sono entrambe {1, a, b, c, ...} (da sinistra a destra, su-giù).

      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

Sapere che un tavolo Cayley è anche un quadrato latino ridotto (a causa della proprietà di annullamento) consente di generare le possibili tabelle riga per riga. A partire dalla seconda riga (indice 1), generiamo tutte le permutazioni uniche per quella riga, mantenendo la prima colonna fissata sul valore dell'indice.

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

Naturalmente non tutte le permutazioni sono valide: ogni riga e colonna deve contenere tutti gli elementi esattamente una volta. A questo scopo viene utilizzato un blocco filtro (un valore di verità mantiene la permutazione, uno di falsa la rimuove):

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

Nota che sto filtrando all'interno del ciclo di generazione: questo rende il codice un po 'più lungo (rispetto a generazione e filtro distinti), ma migliora notevolmente le prestazioni. Il numero di permutazioni di un set di dimensioni n è n! , quindi la soluzione più breve richiederebbe molta memoria e tempo.

Un elenco di tabelle Cayley valide è un grande passo verso l'enumerazione degli operatori, ma essendo una struttura 2D, non può verificare l'associatività, che è una proprietà 3D. Quindi il prossimo passo è filtrare le funzioni non associative.

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

Accidenti! Molto lavoro, ma ora abbiamo elencato tutti i gruppi di ordine n (o meglio, le operazioni su di esso - ma il set è stato risolto, quindi è la stessa cosa). Passaggio successivo: trova gli isomorfismi. Un isomorfismo è una biiezione tra due di quei gruppi tale che φ (x ∗ y) = φ (x) ∗ φ (y) . Generare quelle biiezioni in CJam è banale: Ne!lo farà. Come possiamo controllarli? La mia soluzione parte da due copie della tabella Cayley per x ∗ y . Su una copia, φ viene applicato a tutti gli elementi, senza toccare l'ordine di righe o colonne. Questo genera la tabella per φ (x ∗ y) . Dall'altro gli elementi sono lasciati così come sono, ma le righe e le colonne sono mappate attraverso φ . Cioè, la riga / colonnax diventa la riga / colonna φ (x) . Questo genera la tabella per φ (x) ∗ φ (y) . Ora che abbiamo i due tavoli, non ci resta che confrontarli: se sono uguali, abbiamo trovato un isomorfismo.

Naturalmente, dobbiamo anche generare le coppie di gruppi su cui testare l'isomorfismo. Abbiamo bisogno di tutte le 2 combinazioni dei gruppi. Sembra che CJam non abbia un operatore per le combinazioni. Possiamo generarli prendendo ogni gruppo e combinandolo solo con gli elementi che lo seguono nell'elenco. Curiosità: il numero di 2 combinazioni è n × (n - 1) / 2 , che è anche la somma dei primi n - 1 numeri naturali. Tali numeri sono chiamati numeri triangolari: prova l'algoritmo su carta, una riga per elemento fisso e vedrai perché.

: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

Il codice sopra risolve il primo elemento della coppia, L [X] , e lo combina con altri gruppi (chiamiamo ciascuno di quei Y ). Passa la coppia a un blocco di test di isomorfismo che mostrerò tra poco. Il blocco restituisce un elenco di valori di Y per cui L [X] è isomorfo a Y . Quindi L [X] viene aggiunto a questo elenco. Prima di capire perché le liste sono impostate in questo modo, diamo un'occhiata al test di 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

Bene, ora abbiamo un elenco di set come [{L [0], Y1, Y2, ...}, {L [1], Y1, ...}, ...] . L'idea qui è che, per proprietà transitiva, se due insiemi hanno almeno un elemento in comune, tutti i gruppi nei due insiemi sono isomorfi. Possono essere aggregati in un singolo set. Poiché L [X] non comparirà mai nelle combinazioni generate da L [X + ...] , dopo aver aggregato ogni serie di isomorfismi avrà un elemento unico. Quindi, per ottenere il numero di isomorfismi, è sufficiente contare quanti gruppi compaiono esattamente una volta in tutti gli insiemi di gruppi isomorfici. Per fare ciò, scartare i set in modo che sembrino [L [0], Y1, Y2, ..., L [1], Y1, ...] , ordinare l'elenco per creare cluster dello stesso gruppo e infine 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

È tutto gente.


2
Questo è un diavolo di spiegazione. Bello.
The_Basset_Hound il

1
@The_Basset_Hound ... aaaand ora è finito;)
Andrea Biondo,

Considero la mia risposta non competitiva, quindi ho accettato questa.
Dennis,

4

CJam, 73 byte

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

La complessità temporale del codice sopra è peggiore di O (n! N ) .

L'ingresso n = 4 è già troppo per l' interprete online .

Utilizzando l' interprete Java , è possibile immettere n = 5 , se si dispone di RAM e pazienza sufficienti.

Trovare gruppi

Dato un gruppo (G, ∗) dell'ordine n , possiamo scegliere una biiezione arbitraria φ: G -> C n tale che φ (e) = 0 .

φ diventerà un isomorfismo di (G, ∗) e (C n , ∗ ') se definiamo ∗' per x ∗ 'y = φ (φ -1 (x) ∗ φ -1 (y)) .

Ciò significa che è sufficiente studiare tutti gli operatori di gruppo in C n in modo che 0 sia l'elemento neutro.

Rappresenteremo un operatore di gruppo in C n con una matrice rettangolare T di dimensioni n × n tale che T [x] [y] = x ∗ y .

Per generare un tale array, possiamo iniziare selezionando una permutazione di C n per ciascuna delle sue n righe.

In questo modo, 0 sarà presente in tutte le righe (ma non necessariamente tutte le colonne ), significa che la terza condizione (esistenza di un inverso) si realizzerà, qualunque e può essere.

Possiamo correggere e = 0 richiedendo che la prima colonna di T sia uguale a C n . In particolare, la seconda condizione (esistenza di un elemento neutro) sarà valida.

Per verificare che T corrisponda a un operatore di gruppo, tutto ciò che resta da fare è verificare che la prima condizione (associatività) sia valida. Questo può essere fatto esaurientemente controllando che T [T [x] [y]] [z] == T [x] [T [y] [z]] per tutti x, y, z in C n .

Conteggio di gruppi non isomorfi

Il metodo sopra per trovare gruppi produrrà alcuni gruppi isomorfi. Piuttosto che identificare quali sono isomorfi, generiamo la famiglia di tutti i gruppi isomorfi per ognuno di essi.

Ciò può essere ottenuto eseguendo l'iterazione su tutte le biiezioni φ: C n -> C n e determinando l'array associato , definito da Tφ [x] [y] = φ -1 (T [φ (x)] [φ (y )]) .

Tutto ciò che resta da fare è contare il numero di famiglie distinte.

Cosa fa il codice

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.

Bello. Ho provato un bruto "stupido", ma era difficile arrivare a 5, quindi ho scambiato byte per la velocità.
Andrea Biondo,

1

Python 2 , 515 507 byte

  • Risparmiato otto byte grazie 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)

Provalo online!


Usando l'equivalenza tra il numero di sottogruppi non isomorfi di ordine n di  Σn e il numero di classi di equivalenza isomorfa di gruppi finiti di ordine n.

Link alla versione dettagliata .


Gli ordini di se Gcontano? In caso contrario, è possibile utilizzare def f(k,*s):...f(~-k,j,*s)...e def c(k,*G):...c(~-k,s,*G).....
Dennis,

@Dennis Non lo fanno; Grazie.
Jonathan Frech,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.