Terapia de grupo: identificar grupos

17

Escreva um programa que determine se a tabela de multiplicação do magma finito especificado representa um grupo. Um magma é um conjunto com uma operação binária fechada, o que significa

  • para todos a, b em G, a * b está novamente em G (Fechamento)

Seja (G, *) um magma. (G, *) é um grupo se

  • para todos a, b, c em G, (a * b) * c = a * (b * c) (Associatividade)
  • existe um elemento e em G tal que e * a = a * e = a para todos a em G (Existência de elemento neutro)
  • para todo a em G existe ab em G tal que a * b = b * a = e onde e é o elemento neutro (Existência de Inverso)

Especificações

A entrada é de uma sequência de n ^ 2-1 caracteres (um caractere para cada elemento do magma, permitido é de 0 a 9, az) e representa apenas a tabela lida linha por linha, omitindo o nome do operador. Você pode assumir que a entrada representa um magma válido (que significa que cada um dos elementos aparece exatamente uma vez na linha / coluna do cabeçalho).

Exemplo: Aqui temos a tabela de 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

A sequência de entrada será 012300123112302230133012. (Ou, se usarmos símbolos, também pode ser nezdnnezdeezdnzzdneddnez). Esteja ciente de que a sequência dos elementos na linha e na coluna não precisa ser a mesma, portanto a tabela de Z_4 também pode ter a seguinte aparência:

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

Isso também significa que o elemento neutro não está necessariamente na primeira coluna ou na primeira linha.

Se for um grupo, o programa deve retornar o caractere que representa o elemento neutro. Caso contrário, ele deve retornar um valor falso (diferente dos valores de 0 a 9 az)

Casos de teste

Não-grupos podem ser facilmente construídos alterando apenas um dígito da string ou alterando artificialmente as tabelas que definem uma operação que contradiz um dos axiomas do grupo.

Grupos

Trivial

* | x
-----
x | x

xxx

Neutral Element: x

H (grupo quaternário)

* | 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

Não-Grupos

Um loop (falta de associatividade do grupo ou um quase-grupo com 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)

Um loop IP (de 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 (por Quincunx, obrigado!)

Monóides são magmas com associatividade e um 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

Outro monóide

(Multiplicação mod 10, sem o 5) Obviamente, não temos inversos, e a associatividade é dada pelo módulo de multiplicação 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
flawr
fonte
Pensei em adicionar outra tabela, muito maior, só por diversão: ideone.com/823aRG
Justin
Apenas por diversão, aqui está outra realmente grande que rompe a 0-9a-zregra: ideone.com/vC0ewt
Justin
Para quem não sabe nada sobre grupos, magmas e outros, as especificações são confusas. Por exemplo, as operações são comutativas? (para que a tabela seja redundante). Além disso. a posição de ponto morto, em primeira linha não está relacionado a ter o mesmo fim em linha e coluna: com 10101010o fim é o mesmo e o neutro é na última fila e coluna
edc65
Os grupos @edc não são necessariamente comutativos (os grupos comutativos são chamados abelianos). A definição do grupo a está completa (é a definição usual), qualquer coisa adicional forneceria restrições adicionais. Nessas tabelas, a multiplicação com o elemento neutro geralmente está na primeira linha / coluna, e a sequência dos elementos do cabeçalho da linha / coluna geralmente é a mesma, mas você ainda pode escrever uma tabela válida sem seguir essas convenções, que é o que eu queria incluir aqui.
flawr
11
Eu apaguei alguns comentários que pareciam obsoletos. Por favor, avise-me sobre quaisquer comentários que devam ser retirados.
Martin Ender

Respostas:

4

Oitava, 298 290 270 265 caracteres

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: Removida a alça de função desnecessária.

270: Afinal, a verificação de que e==hpara e sempre satisfazendo e · a = a e h sempre satisfazendo a · h = a não era necessária. Não é possível que sejam diferentes ( e · h =? ).

Os detalhes da explicação para a solução abaixo ainda são relevantes.


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);

A primeira linha

c=@sortrows;d=a=c(c(reshape(a=[0 s],b=numel(a)^.5,b)')'); apenas armazena a entrada na tabela nxn (com caractere zero no local da operação) e, em seguida, classifica lexicograficamente colunas e linhas, para que as linhas e colunas recebam a mesma ordem:

+ | 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

Agora, refiz "a","b","t","z"para o padrão 1, 2, 3, 4, para poder indexar a tabela com eficiência. Isso é feito pela linha for i=2:b a(a==a(i))=i-1;end;. Produz tabela como

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

, onde podemos nos livrar da primeira linha e coluna com 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

Esta tabela possui as propriedades fornecidas:

  • se o elemento neutro e existir, exatamente uma ( isscalar) linha e uma coluna terão o valor do vetor da linha u=[1 2 3 ... number-of-elements]:

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

  • se cada elemento a tem um elemento reverso a ' , duas coisas são válidas: o elemento neutro e ocorre apenas uma vez em cada coluna e apenas uma vez em cada linha ( sum(t=a==e)==1) e, para satisfazer a' · a = a · a ' , as ocorrências de e são simétrico em relação à traduçãot==t'

  • a · b pode ser recuperado por simples t(a,b)indexação. Em seguida, verificamos a associatividade no ciclo chato:

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

A função retorna o elemento neutro da maneira como apareceu na tabela original ( e=d(e+1)) ou em caractere nulo se a tabela não descrever um grupo.

pawel.boczarski
fonte
2
Bem feito e bem explicado. Deve retornar o elemento neutro em vez de 1.
edc65
Corrigido, agora retorna o valor adequado.
precisa saber é o seguinte
11
OCTAVE FTW =) Não tenho certeza sobre duas coisas (provenientes do matlab), mas talvez você possa usá-lo para melhorar sua resposta: Poderia `a (f (a == a (i))) = i-1` ser reduzida para a(a==a(i))=i-1? Fora isso, talvez você possa usar em (...)^.5vez de sqrt(...).
flawr
@ flawr Obrigado, ambos trabalham na oitava (versão 3.8.1).
precisa saber é o seguinte
6

Ruby, 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}

Este é o meu primeiro programa ruby! Isso define uma função lambda que podemos testar fazendo puts f[gets.chomp]. Volto falsepelo meu valor falso. A primeira metade da função é simplesmente analisar a entrada em um mapa, e a segunda metade verifica as possibilidades.

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
}
Justin
fonte
5
Bem-vindo às maravilhas do golfe em Ruby! ;) nilé um valor falsy menor que false. As funções podem ser definidas como lambdas q=->{abort'false'}(se elas aceitarem parâmetros, use-as []para chamá-las em vez de ()). Eu acredito .charsque já deve lhe dar uma matriz, então não há necessidade .to_a. Se você não precisar de uma nova linha à direita, $><<é um byte menor que o putsespaço positivo. Hash.newnão precisa dos parênteses. É tudo o que posso ver por enquanto. Mantem! ;)
Martin Enders
A charscoisa é estranha. Qual versão do Ruby você está usando?
Martin Ender
@ MartinBüttner 1.9.3
Justin
Ah, certo, eu estive olhando a documentação do 2.1.5.
Martin Ender
11
Você pode substituir Math.sqrt(...)por ...**0.5. Além disso, a if bpode ser reescrita: b&&apara evitar os dois espaços
Cristian Lupascu
4

JavaScript (ES6) 285 278

Execute o snippet para testar (sendo o ES6, ele funciona apenas no Firefox)

Editar 2 Correção bug. Eu estava errado em encontrar o elemento neutro, verificando apenas uma maneira. (Precisa de melhores casos de teste !!!)

Editar Usando concatenação de string mais simples em vez de índice duplo (como @Quincunx), não sei o que estava pensando. Além disso, verificação inversa simplificada, ainda deve funcionar.

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>

edc65
fonte
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

Amaldiçoe aqueles 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

Explicação

f::String->Stringmapeia a sequência para e::Charo elemento de identidade ou !.

A wherecláusula cria um monte de variáveis ​​e funções que eu comentei; v::[Int]é a lista vertical de elementos, h::[Int]a horizontal.

%::Char->Char->Char aplica a operação de grupo aos seus argumentos.

g::[[Int]]é a tabela de grupo (para remover a referência usando %)

j::Maybe Intcontém o índice da identidade, vse existir, caso contrário Nothing, é por isso que isJust jestá a condição da fidentidade.

alexander-brett
fonte
Você pode explicar um pouco o que está acontecendo aqui?
Xebtl
Adicionei alguns comentários, mas a essência básica é 'aplique os testes à tabela de grupo'. Note que {- -}é um comentário. Você tem perguntas mais específicas ou isso esclarece?
Alexander-brett
Obrigado. Eu acho que para realmente entender o que eu precisaria aprender algumas Haskell primeira :-)
xebtl