Terapia di gruppo: identificare i gruppi


17

Scrivi un programma, che determina se la tabella di moltiplicazione del dato magma finito rappresenta un gruppo. Un magma è un insieme con un'operazione binaria chiusa, ciò significa

  • per tutte a, b in G, a * b è di nuovo in G (Chiusura)

Sia (G, *) un magma. (G, *) è un gruppo se

  • per tutte a, b, c in G, (a * b) * c = a * (b * c) (associatività)
  • esiste un elemento e in G tale che e * a = a * e = a per tutti a in G (Esistenza di elemento neutro)
  • per tutti a in G c'è ab in G tale che a * b = b * a = e dove e è l'elemento neutro (Esistenza di inversa)

Specifiche

L'input è costituito da una stringa di n ^ 2-1 caratteri (un carattere per ogni elemento del magma, sono ammessi 0-9, az) e rappresenta solo la tabella letta riga per riga, inserendo il nome dell'operatore. Puoi presumere che l'input rappresenti un magma valido (ciò significa che ciascuno degli elementi appare esattamente una volta nella riga / colonna dell'intestazione).

Esempio: qui abbiamo la tabella di Z_4

+ | 0 1 2 3
-----------
0 | 0 1 2 3
1 | 1 2 3 0
2 | 2 3 0 1
3 | 3 0 1 2

La stringa di input sarà 012300123112302230133012. (O se usiamo simboli potrebbe anche essere nezdnnezdeezdnzzdneddnez). Tieni presente che la sequenza degli elementi nella riga e nella colonna non deve essere la stessa, quindi anche la tabella di Z_4 potrebbe apparire così:

+ | 1 3 2 0
-----------
1 | 2 0 3 1
0 | 1 3 2 0
2 | 3 1 0 2
3 | 0 2 1 3

Ciò significa anche che l'elemento neutro non si trova necessariamente nella prima colonna o nella prima riga.

Se è un gruppo, il programma deve restituire il carattere che rappresenta l'elemento neutro. In caso contrario, deve restituire un valore falso (distinto dai valori 0-9 az)

Casi test

I non gruppi possono essere facilmente costruiti semplicemente alterando una cifra della stringa o alterando artificialmente le tabelle definendo un'operazione che contraddice uno degli assiomi del gruppo.

gruppi

Banale

* | x
-----
x | x

xxx

Neutral Element: x

H (gruppo quaternione)

* | p t d k g b n m 
-------------------
m | b d t g k p m n 
p | m k g d t n p b 
n | p t d k g b n m 
b | n g k t d m b p 
t | g m n p b k t d 
d | k n m b p g d t 
k | t b p m n d k g 
g | d p b n m t g k 

ptdkgbnmmbdtgkpmnpmkgdtnpbnptdkgbnmbngktdmbptgmnpbktddknmbpgdtktbpmndkggdpbnmtgk

Neutral Element: n

D_4

* | y r s t u v w x
-------------------
u | u x w v y t s r
v | v u x w r y t s
w | w v u x s r y t
x | x w v u t s r y
y | y r s t u v w x
r | r s t y v w x u
s | s t y r w x u v
t | t y r s x u v w


yrstuvwxuuxwvytsrvvuxwrytswwvuxsrytxxwvutsryyyrstuvwxrrstyvwxusstyrwxuvttyrsxuvw

Neutral Element: y

Z_6 x Z_2

x | 0 1 2 3 5 7 8 9 a b 4 6
---------------------------
0 | 0 1 2 3 5 7 8 9 a b 4 6 
1 | 1 2 3 4 0 8 9 a b 6 5 7 
2 | 2 3 4 5 1 9 a b 6 7 0 8 
7 | 7 8 9 a 6 2 3 4 5 0 b 1 
8 | 8 9 a b 7 3 4 5 0 1 6 2 
9 | 9 a b 6 8 4 5 0 1 2 7 3 
a | a b 6 7 9 5 0 1 2 3 8 4 
b | b 6 7 8 a 0 1 2 3 4 9 5 
3 | 3 4 5 0 2 a b 6 7 8 1 9 
4 | 4 5 0 1 3 b 6 7 8 9 2 a 
5 | 5 0 1 2 4 6 7 8 9 a 3 b 
6 | 6 7 8 9 b 1 2 3 4 5 a 0 

01235789ab46001235789ab4611234089ab6572234519ab67087789a623450b1889ab7345016299ab684501273aab6795012384bb678a0123495334502ab67819445013b67892a5501246789a3b66789b12345a0

Neutral Element: 0

A_4

* | i a b c d e f g h j k l
---------------------------
i | i a b c d e f g h j k l
a | a b i e c d g h f l j k
b | b i a d e c h f g k l j
c | c f j i g k a d l b e h
d | d h k b f l i e j a c g
e | e g l a h j b c k i d f
f | f j c k i g d l a h b e
g | g l e j a h c k b f i d
h | h k d l b f e j i g a c
j | j c f g k i l a d e h b
k | k d h f l b j i e c g a
l | l e g h j a k b c d f i

iabcdefghjkliiabcdefghjklaabiecdghfljkbbiadechfgkljccfjigkadlbehddhkbfliejacgeeglahjbckidfffjckigdlahbegglejahckbfidhhkdlbfejigacjjcfgkiladehbkkdhflbjiecgalleghjakbcdfi

Neutral Element: i

Non-Gruppi

Un ciclo (Associatività mancante di gruppo o Quasi-gruppo con elemento neutro)

* | 1 2 3 4 5
-------------
1 | 1 2 3 4 5 
2 | 2 4 1 5 3 
3 | 3 5 4 2 1 
4 | 4 1 5 3 2 
5 | 5 3 2 1 4

12345112345224153335421441532553214

Neutral Element: 1
(2*2)*3 = 4*3 = 5 != 2 = 2*1 = 2*(2*3)

Un loop IP (da http://www.quasigroups.eu/contents/download/2008/16_2.pdf )

* | 1 2 3 4 5 6 7
-----------------
1 | 1 2 3 4 5 6 7
2 | 2 3 1 6 7 5 4
3 | 3 1 2 7 6 4 5
4 | 4 7 6 5 1 2 3
5 | 5 6 7 1 4 3 2
6 | 6 4 5 3 2 7 1
7 | 7 5 4 2 3 1 6

123456711234567223167543312764544765123556714326645327177542316

Neutral Element: 1
2*(2*4) = 2*6 = 5 != 7 = 3*4 = (2*2)*4

Monoid (di Quincunx, grazie!)

I monoidi sono magmi con associatività e un elemento neutro.

* | 0 1 2 3
-----------
0 | 0 1 2 3
1 | 1 3 1 3
2 | 2 1 0 3
3 | 3 3 3 3

012300123113132210333333

Neutral Element: 0

Un altro monoide

(Moltiplicazione mod 10, senza il 5) Ovviamente non abbiamo inversioni e l'associatività è data dalla moltiplicazione modulo 10.

* | 1 2 3 4 6 7 8 9
-------------------
1 | 1 2 3 4 6 7 8 9
2 | 2 4 6 8 2 4 6 8
3 | 3 6 9 2 8 1 4 7
4 | 4 8 2 6 4 8 2 6
6 | 6 2 8 4 6 2 8 4
7 | 7 4 1 8 2 9 6 3
8 | 8 6 4 2 8 6 4 2
9 | 9 8 7 6 4 3 2 1

Neutral Element: 1   12346789112346789224682468336928147448264826662846284774182963886428642998764321

Pensavo di aggiungere un altro tavolo, molto più grande, solo per divertimento: ideone.com/823aRG
Justin

Solo per divertimento, eccone un altro davvero grande che infrange la 0-9a-zregola: ideone.com/vC0ewt
Justin

Per uno che non sa nulla di gruppi, magmi e così via, le specifiche sono sfocate. Ad esempio, le operazioni sono commutative? (quindi la tabella è ridondante). Inoltre. la posizione di neutro in prima fila non è correlata allo stesso ordine in riga e colonna: con 10101010l'ordine è lo stesso e il neutro è nell'ultima riga e colonna
edc65

I gruppi @edc non sono necessariamente commutativi (i gruppi commutativi sono chiamati abeliani). La definizione di un gruppo è completa (è la solita definizione), qualsiasi cosa aggiuntiva fornirebbe ulteriori restrizioni. In quelle tabelle la moltiplicazione con l'elemento neutro si trova generalmente nella prima riga / colonna e la sequenza degli elementi dell'intestazione riga / colonna è generalmente la stessa, ma è comunque possibile scrivere una tabella valida senza seguire quelle convenzioni, che è quello che volevo includere qui.
Flawr,

1
Ho cancellato alcuni commenti che sembravano obsoleti. Vi prego di avvisarmi di eventuali commenti che dovrebbero essere eliminati.
Martin Ender,

Risposte:


4

Ottava, 298 290 270 265 caratteri

function e=g(s)
c=@sortrows;d=a=c(c(reshape(a=[0 s],b=numel(a)^.5,b)')');
for i=2:b a(a==a(i))=i-1;end;
a=a(2:b,2:b--);u=1:b;
e=(isscalar(e=find(all(a==u')))&&a(e,:)==u&&sum(t=a==e)==1&&t==t')*e;
for x=u for y=u for z=u e*=a(a(x,y),z)==a(x,a(y,z));end;end;end;e=d(e+1);

265: rimossa la maniglia di funzione non necessaria.

270: Dopo tutto, il controllo che e==hper e sempre soddisfacente e · a = a e h sempre soddisfacente a · h = a non era necessario. Questo non è possibile per loro di essere diversi ( e · h =? ).

I dettagli della spiegazione per la soluzione di seguito sono ancora rilevanti.


290:

function e=g(s)
c=@sortrows;d=a=c(c(reshape(a=[0 s],b=numel(a)^.5,b)')');
for i=2:b a(a==a(i))=i-1;end;
a=a(2:b,2:b--);u=1:b;
s=@isscalar;e=(s(e=find(all(a==u')))&&s(h=find(all(a'==u')'))&&sum(t=a==e)==1&&t==t')*e;
for x=u for y=u for z=u e*=a(a(x,y),z)==a(x,a(y,z));end;end;end;e=d(e+1);

La prima riga

c=@sortrows;d=a=c(c(reshape(a=[0 s],b=numel(a)^.5,b)')'); memorizza semplicemente l'input nella tabella nxn (con carattere zero nel punto in cui è indicato il punto operativo), quindi ordina lessicograficamente colonne e righe, in modo che righe e colonne ricevano lo stesso ordine:

+ | z a t b                        + | a b t z
-----------                        -----------
z | t b a z         becomes        a | t a z b
b | z a t b      ============>     b | a b t z
t | a z b t                        t | z t b a
a | b t z a                        z | b z a t

Ora rimappo "a","b","t","z"allo standard 1, 2, 3, 4, in modo da poter indicizzare la tabella in modo efficiente. Questo è fatto dalla linea for i=2:b a(a==a(i))=i-1;end;. Produce tabella come

0   1   2   3   4
1   3   1   4   2
2   1   2   3   4
3   4   3   2   1
4   2   4   1   3

, dove possiamo eliminare la prima riga e colonna con a=a(2:b,2:b--);u=1:b;:

3  1  4  2
1  2  3  4
4  3  2  1
2  4  1  3

Questa tabella ha le proprietà indicate:

  • se esiste l'elemento neutro e , esattamente una ( isscalar) riga e una colonna hanno il valore del vettore riga u=[1 2 3 ... number-of-elements]:

s=@isscalar;e=(s(e=find(all(a==u')))&&s(h=find(all(a'==u')'))&&...

  • se ogni elemento a ha un elemento inverso a ' , due cose valgono: l'elemento neutro e si verifica solo una volta ogni colonna e solo una volta ogni riga ( sum(t=a==e)==1) e, per soddisfare un' · a = a · a ' , le occorrenze di e sono simmetrico rispetto alla traduzionet==t'

  • a · b può essere recuperato mediante una semplice t(a,b)indicizzazione. Quindi controlliamo l'associatività nel loop noioso:

for x=u for y=u for z=u e*=a(a(x,y),z)==a(x,a(y,z));end;end;end;

La funzione restituisce l'elemento neutro come appariva nella tabella originale ( e=d(e+1)) o nel carattere nullo se la tabella non descrive un gruppo.


2
Ben fatto e ben spiegato. Dovrebbe restituire l'elemento neutro anziché 1.
edc65

Corretto, ora restituisce il valore corretto.
pawel.boczarski,

1
OCTAVE FTW =) Non sono sicuro di due cose (provenienti da Matlab), ma forse puoi usarlo per migliorare la tua risposta: Potrebbe essere `a (f (a == a (i))) = i-1` a a(a==a(i))=i-1? Diverso da quello che puoi usare al (...)^.5posto di sqrt(...).
flawr,

@flawr Grazie, funzionano entrambi in ottava (versione 3.8.1).
pawel.boczarski,

6

Rubino, 401 ... 272

f=->s{n=(s.size+1)**0.5
w=n.to_i-1
e=s[0,w].split''
s=s[w,n*n]
m={}
w.times{(1..w).each{|i|m[s[0]+e[i-1]]=s[i]}
s=s[n,n*n]}
s=e.find{|a|e.all?{|b|x=m[a+b]
x==m[b+a]&&x==b}}
e.all?{|a|t=!0
e.all?{|b|x=m[a+b]
t||=x==m[b+a]&&x==s
e.all?{|c|m[m[a+b]+c]==m[a+m[b+c]]}}&&t}&&s}

Questo è il mio primo programma di rubini! Questo definisce una funzione lambda che possiamo testare facendo puts f[gets.chomp]. Ritorno falseper il mio falso valore. La prima metà della funzione sta semplicemente analizzando l'input in una mappa, quindi la seconda metà verifica le possibilità.

f=->s{
    n=((s.size+1)**0.5).to_i
    w=n-1
    e=s[0,w].split'' # create an array of elements of the potential group
    s=s[w,n*n]
    m={} # this map is what defines our operation
    w.times{
        (1..w).each{               # for each element in the row of the table
            |i|m[s[0]+e[i-1]]=s[i] # put the value into the map
        }
        s=s[n,n*n]
    }
    s=e.find{|a| # s is the identity
        e.all?{|b|
            x=m[a+b]
            x==m[b+a]&&x==b # is a the identity?
        }
    }
    e.all?{|a| # implicit return statement
        t = !0 # t = false
        e.all?{|b| # check for inverses
            x=m[a+b]
            t ||= x==m[b+a]&&x==s # t is now true if b was a's inverse
            e.all?{|c|
                m[m[a+b]+c]==m[a+m[b+c]] # check associativity
            }
        } && t
    }&&s
}

5
Benvenuti nelle meraviglie del golf a Ruby! ;) nilè un valore di falsy più breve di false. Le funzioni possono essere definite come lambda q=->{abort'false'}(se accettano parametri, quindi usano []per chiamarli invece di ()). Credo che .charsdovrebbe già darti un array, quindi non è necessario .to_a. Se non hai bisogno di una nuova riga finale $><<è un byte più corto di putspiù spazio. Hash.newnon ha bisogno delle parentesi. Questo è tutto ciò che posso vedere per ora. Continuate così! ;)
Martin Ender,

La charscosa è strana. Quale versione di Ruby stai usando?
Martin Ender,

@ MartinBüttner 1.9.3
Justin,

Ah, giusto, ho visto la documentazione della 2.1.5.
Martin Ender,

1
È possibile sostituire Math.sqrt(...)con ...**0.5. Inoltre, a if bpuò essere riscritto: b&&aper evitare i due spazi
Cristian Lupascu,

4

JavaScript (ES6) 285243 278

Esegui snippet per testare (essendo ES6 funziona solo su Firefox)

Modifica 2 Correzione bug. Ho sbagliato a trovare l'elemento neutro, controllando solo un modo. (Hai bisogno di migliori casi di test !!!)

Modifica Usando la concatenazione di stringhe più semplice invece del doppio indice (come @Quincunx), non so cosa stavo pensando. Inoltre, controllo inverso semplificato, dovrebbe comunque funzionare.

F=t=>(
  e=t.slice(0,d=Math.sqrt(t.length)|0),
  t=t.slice(d).match('.'.repeat(d+1),'g'),
  t.map(r=>{
    for(v=r[i=0],
        j=e.search(v)+1, // column for current row  element
        r!=v+e|t.some(r=>r[j]!=r[0])?0:n=v; // find neutral
        c=r[++i];
       )h[v+e[i-1]]=c
  },h={},n=''),
  e=[...e],!e.some(a=>e.some(b=>(
    h[a+b]==n&&--d, // inverse
    e.some(c=>h[h[a+b]+c]!=h[a+h[b+c]]) // associativity
  )
  ))&&!d&&n
)
input { width: 400px; font-size:10px }
Click on textbox to test - Result : <span id=O></span><br>
<input value='...' onclick='O.innerHTML=F(this.value)'> (?)
<br>Groups<br>
<input value='nezdnnezdeezdnzzdneddnez' onclick='O.innerHTML=F(this.value)'> (n)<br>
<input value='ptdkgbnmmbdtgkpmnpmkgdtnpbnptdkgbnmbngktdmbptgmnpbktddknmbpgdtktbpmndkggdpbnmtgk' onclick='O.innerHTML=F(this.value)'> (n)<br>
<input value='yrstuvwxuuxwvytsrvvuxwrytswwvuxsrytxxwvutsryyyrstuvwxrrstyvwxusstyrwxuvttyrsxuvw' onclick='O.innerHTML=F(this.value)'> (y)<br>
<input value='01235789ab46001235789ab4611234089ab6572234519ab67087789a623450b1889ab7345016299ab684501273aab6795012384bb678a0123495334502ab67819445013b67892a5501246789a3b66789b12345a0'onclick='O.innerHTML=F(this.value)'> (0)<br>
Non groups <br>
<input value='12345112345224153335421441532553214' onclick='O.innerHTML=F(this.value)'> (FAIL)<br>
<input value='123456711234567223167543312764544765123556714326645327177542316' onclick='O.innerHTML=F(this.value)'> (FAIL)<br>
<input value='012300123113132210333333' onclick='O.innerHTML=F(this.value)'> (FAIL)<br>


2

Haskell 391B

import Data.Maybe
import Data.List
o a b=elemIndex b a
l£a=fromJust.o a$l
a§b=[a!!i|i<-b]
f s|isJust j&&and(map(isJust.o h)s)&&and[or[p%q==e|q<-h]&&and[p%(q%r)==(p%q)%r|q<-h,r<-h]|p<-h]=[e]|True="!"where n=floor$(sqrt(fromIntegral$length s+1))-1;h=take n s;g=[s§[a..b]|(a,b)<-zip[1+n,2+n+n..][n+n,3*n+1..(n+1)^2]];v=s§[n,1+2*n..n+n*n];a%b=g!!(b£v)!!(a£h);j=o g h;e=v!!fromJust j

Maledici quelli import!

import Data.Maybe
import Data.List

{- rename elemIndex to save characters -}
o a b=elemIndex b a

{- get the index of l in a -}
l£a=fromJust.o a$l

{- extract a sublist of a with indices b -}
a§b=[a!!i|i<-b]

f s |isJust j {-Identity-}
     &&and (map (isJust.o h) s) {-Closure-}
     &&and[
        or [p%q==e|q<-h] {-Inverse-}
        && and [ p%(q%r)==(p%q)%r | q<-h,r<-h ] {-Associativity-}
     |
        p<-h
     ]=[e]
    |True="!"
    where
    {-size-}    n=floor$(sqrt(fromIntegral$length s+1))-1
    {-horiz-}   h=take n s
    {-table-}   g=[s§[a..b]|(a,b)<-zip[1+n,2+n+n..][n+n,3*n+1..(n+1)^2]]
    {-vert-}    v=s§[n,1+2*n..n+n*n]
    {-operate-} a%b=g!!(b£v)!!(a£h)
                j=o g h {-index of the first row identical to the top-}
    {-ident-}   e=v!!fromJust j

Spiegazione

f::String->Stringassocia la stringa a e::Char, l'elemento di identità o !.

La whereclausola crea un mucchio di variabili e funzioni, che ho commentato; v::[Int]è l'elenco verticale degli elementi, h::[Int]quello orizzontale.

%::Char->Char->Char applica l'operazione di gruppo ai suoi argomenti.

g::[[Int]]è la tabella di gruppo (per la dereferenziazione mediante %)

j::Maybe Intcontiene l'indice dell'identità vse esiste, altrimenti Nothing, ecco perché isJust jè la condizione fper l'identità.


Puoi spiegarci un po 'cosa sta succedendo qui?
xebtl,

Ho aggiunto alcuni commenti, ma l'essenza di base è "applica i test alla tabella dei gruppi". Si noti che {- -}è un commento. Hai domande più specifiche o lo chiarisci?
alexander-brett,

Grazie. Immagino che per capirlo davvero dovrei prima imparare un po 'di Haskell :-)
xebtl
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.