Gere um quadrado greco-latino


24

Isenção de responsabilidade: não conheço nenhuma solução que não seja da bruteforce

Um quadrado graeco-latino é, para dois conjuntos do mesmo comprimento , um arranjo de células, cada um contendo um par único (em todo o quadrado) de um elemento do primeiro conjunto e um elemento do segundo conjunto, de modo que todos os primeiros elementos e todos os segundos elementos dos pares sejam exclusivos em suas linhas e colunas. Os conjuntos mais comuns usados ​​são, como se poderia imaginar, as primeiras letras dos alfabetos grego e latino.nn×nn

Aqui está uma foto de uma praça greco-latina 4x4:insira a descrição da imagem aqui

Os quadrados greco-latinos são tão úteis quanto parecem (o artigo da Wikipedia menciona "design de experimentos, programação de torneios e construção de quadrados mágicos"). Sua tarefa é, dado um número inteiro positivo , gerar um quadrado graeco-latino.nn×n

Entrada

Um número inteiro positivo ; é garantido que exista quadrado greco-latino (ou seja, ).n>2n×nn6

Saída

Um quadrado graeco-latino com comprimento lateral n como uma matriz bidimensional, uma matriz de matrizes, uma matriz achatada ou produzida diretamente.

Notas

  • Você não precisa usar os alfabetos grego e latino especificamente; por exemplo, a saída de pares de números inteiros positivos também é permitida.
  • Se você optar por usar um alfabeto que não pode ser estendido arbitrariamente, precisará (teoricamente; seu código não precisa terminar antes da morte pelo calor do universo) para suportar um comprimento máximo lateral de pelo menos 20.

Isso é , então o código mais curto vence!



Temos que gerar um único quadrado ou está correto gerar todos os quadrados possíveis como uma lista?
Nick Kennedy

Respostas:


2

Geléia ,  21  20 bytes

-1 graças a Nick Kennedy (a opção de saída plana permite uma economia de bytes de )ż"þ`ẎẎQƑ$Ƈ F€p`Z€QƑƇ

Œ!ṗ⁸Z€Q€ƑƇF€p`Z€QƑƇḢ

Experimente online! (Muito lento para os4anos 60 no TIO, mas se substituirmos o poder cartesiano, por Combinaçõesœc, ele será concluído - embora 5 certamente não o seja!)

Quão?

Œ!ṗ⁸Z€Q€ƑƇF€p`Z€QƑƇḢ - Link: integer, n
Œ!                   - all permutations of [1..n]
   ⁸                 - chain's left argument, n
  ṗ                  - Cartesian power (that is, all ways to pick n of those permutations, with replacement, not ignoring order)
    Z€               - transpose each
         Ƈ           - filter, keeping those for which:
        Ƒ            -   invariant under:
      Q€             -     de-duplicate each
          F€         - flatten each  
             `       - use this as both arguments of:
            p        -   Cartesian product
              Z€     - transpose each
                  Ƈ  - filter, keeping those for which:
                 Ƒ   -   invariant under:   
                Q    -     de-duplicate (i.e. contains all the possible pairs)
                   Ḣ - head (just one of the Latin-Greaco squares we've found)

Aqui está um 20 . Originalmente, escrevi isso independentemente do seu, mas acabei com algo bem parecido e, em seguida, me inspirei no uso do poder cartesiano no lugar de uma díade de permutação, por isso é provavelmente melhor usá-lo para melhorar o seu. Observe que você digitou incorretamente o Graeco em sua explicação.
Nick Kennedy

Obrigado Nick, eu não percebi que estávamos autorizados a produzir uma versão achatada.
Jonathan Allan


3

R , 164 148 bytes

-muitos bytes graças a Giuseppe.

n=scan()
`!`=function(x)sd(colSums(2^x))
m=function()matrix(sample(n,n^2,1),n)
while(T)T=!(l=m())|!(g=m())|!t(l)|!t(g)|1-all(1:n^2%in%(n*l+g-n))
l
g

Experimente online!

Dramaticamente ineficiente - acho que é ainda pior do que outras abordagens de força bruta. Mesmo n=3assim, provavelmente o tempo limite será atingido no TIO. Aqui está uma versão alternativa (155 bytes) que funciona n=3em cerca de 1 segundo.

m1 1nnlg

  1. all(1:n^2%in%(n*l+g-n))n2l × g
  2. são le gquadrados latinos?

!nlg2^l2n+1 1-2lt(l)lgsdn=0 0n=1 1

Uma observação final: tantas vezes no código R do golfe, usei a variável Tinicializada como TRUEpara obter alguns bytes. Mas isso significa que, quando eu precisava do valor real TRUEna definição de m(parâmetro replacein sample), precisava usar em 1vez de T. Da mesma forma, como estou redefinindo !como uma função diferente da negação, tive que usar em 1-all(...)vez de !all(...).


2

JavaScript (ES6),  159 147  140 bytes

n×n

Esta é uma pesquisa simples de força bruta e, portanto, muito lenta.

n=>(g=(m,j=0,X=n*n)=>j<n*n?!X--||m.some(([x,y],i)=>(X==x)+(Y==y)>(j/n^i/n&&j%n!=i%n),g(m,j,X),Y=X/n|0,X%=n)?o:g([...m,[X,Y]],j+1):o=m)(o=[])

Experimente online! (com saída prettificada)

Comentado

n => (                      // n = input
  g = (                     // g is the recursive search function taking:
    m,                      //   m[] = flattened matrix
    j = 0,                  //   j   = current position in m[]
    X = n * n               //   X   = counter used to compute the current pair
  ) =>                      //
    j < n * n ?             // if j is less than n²:
      !X-- ||               //   abort right away if X is equal to 0; decrement X
      m.some(([x, y], i) => //   for each pair [x, y] at position i in m[]:
        (X == x) +          //     yield 1 if X is equal to x OR Y is equal to y
        (Y == y)            //     yield 2 if both values are equal
                            //     or yield 0 otherwise
        >                   //     test whether the above result is greater than:
        ( j / n ^ i / n &&  //       - 1 if i and j are neither on the same row
          j % n != i % n    //         nor the same column
        ),                  //       - 0 otherwise
                            //     initialization of some():
        g(m, j, X),         //       do a recursive call with all parameters unchanged
        Y = X / n | 0,      //       start with Y = floor(X / n)
        X %= n              //       and X = X % n
      ) ?                   //   end of some(); if it's falsy (or X was equal to 0):
        o                   //     just return o[]
      :                     //   else:
        g(                  //     do a recursive call:
          [...m, [X, Y]],   //       append [X, Y] to m[]
          j + 1             //       increment j
        )                   //     end of recursive call
    :                       // else:
      o = m                 //   success: update o[] to m[]
)(o = [])                   // initial call to g with m = o = []

144 ? (No meu telefone, para não ter certeza de que funciona)
Shaggy

Também não acho que você precise o; você pode simplesmente retornar mno final para 141
Shaggy

n=5

2

Haskell , 207 143 233 bytes

(p,q)!(a,b)=p/=a&&q/=b
e=filter
f n|l<-[1..n]=head$0#[(c,k)|c<-l,k<-l]$[]where
	((i,j)%p)m|j==n=[[]]|1>0=[q:r|q<-p,all(q!)[m!!a!!j|a<-[0..i-1]],r<-(i,j+1)%e(q!)p$m]
	(i#p)m|i==n=[[]]|1>0=[r:o|r<-(i,0)%p$m,o<-(i+1)#e(`notElem`r)p$r:m]

Experimente online!

OK, acho que finalmente entendi desta vez. Ele funciona bem para n = 5, n = 6 vezes no TIO, mas acho que pode ser porque esse novo algoritmo é INCRÍVEL, ineficiente e basicamente verifica todas as possibilidades até encontrar um que funcione. Agora estou executando n = 6 no meu laptop para ver se ele termina com mais algum tempo.

Mais uma vez obrigado a @oneone por apontar os bugs nas minhas versões anteriores


11
Não conheço Haskell, mas isso parece erro para mim quando altero o "4" no rodapé para 5. Estou invocando isso corretamente?
meu pronome é monicareinstate

@someone Boa captura, eu deveria ter testado isso. Na verdade, não tenho certeza do que está acontecendo de errado aqui, isso pode demorar um pouco para depurar
user1472751

11
Eu acho que isso ainda tem um bug; quando executado para n = 5, a tupla (1,1) aparece duas vezes.
meu pronome é monicareinstate

@ Alguém Alguém, esse problema é muito mais difícil do que eu pensava. Não consigo encontrar uma maneira confiável de bloquear todas as restrições de uma só vez. Assim que eu me concentro, um sai do meu alcance. Por enquanto, vou marcar como não concorrente até encontrar mais tempo para trabalhar nisso. Desculpe por não testar tão bem quanto eu deveria ter
user1472751

1

C #, 520 506 494 484 bytes

class P{static void Main(string[]a){int n=int.Parse(a[0]);int[,,]m=new int[n,n,2];int i=n,j,k,p,I,J;R:for(;i-->0;)for(j=n;j-->0;)for(k=2;k-->0;)if((m[i,j,k]=(m[i,j,k]+ 1) % n)!=0)goto Q;Q:for(i=n;i-->0;)for(j=n;j-->0;){for(k=2;k-->0;)for(p=n;p-->0;)if(p!=i&&m[i,j,k]==m[p,j,k]||p!=j&&m[i,j,k]==m[i,p,k])goto R;for(I=i;I<n;I++)for(J=0;J<n;J++)if(I!=i&&J!=j&&m[i,j,0]==m[I,J,0]&&m[i,j,1]==m[I,J,1])goto R;}for(i=n;i-->0;)for(j=n;j-->0;)System.Console.Write(m[i,j,0]+"-"+m[i,j,1]+" ");}}

O algoritmo de encontrar um quadrado é muito simples. É ... força bruta. Sim, é estúpido, mas o código de golfe não é sobre a velocidade de um programa, certo?

O código antes de torná-lo mais curto:

using System;

public class Program
{
    static int[,,] Next(int[,,] m, int n){
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                for (int k = 0; k < 2; k++)
                {
                    if ((m[i, j, k] = (m[i, j, k] + 1) % n) != 0)
                    {
                        return m;
                    }
                }
            }
        }
        return m;
    }
    static bool Check(int[,,] m, int n)
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                for (int k = 0; k < 2; k++)
                {
                    for (int p = 0; p < n; p++)
                    {
                        if (p != i)
                            if (m[i, j, k] == m[p, j, k])
                                return false;
                    }
                    for (int p = 0; p < n; p++)
                    {
                        if (p != j)
                            if (m[i, j, k] == m[i, p, k])
                                return false;
                    }
                }
            }
        }

        for (int i_1 = 0; i_1 < n; i_1++)
        {
            for (int j_1 = 0; j_1 < n; j_1++)
            {
                int i_2 = i_1;
                for (int j_2 = j_1 + 1; j_2 < n; j_2++)
                {
                    if (m[i_1, j_1, 0] == m[i_2, j_2, 0] && m[i_1, j_1, 1] == m[i_2, j_2, 1])
                        return false;
                }
                for (i_2 = i_1 + 1; i_2 < n; i_2++)
                {
                    for (int j_2 = 0; j_2 < n; j_2++)
                    {
                        if (m[i_1, j_1, 0] == m[i_2, j_2, 0] && m[i_1, j_1, 1] == m[i_2, j_2, 1])
                            return false;
                    }
                }
            }
        }
        return true;
    }
    public static void Main()
    {
        int n = 3;
        Console.WriteLine(n);
        int maxi = (int)System.Math.Pow((double)n, (double)n*n*2);
        int[,,] m = new int[n, n, 2];
        Debug(m, n);
        do
        {
            m = Next(m, n);
            if (m == null)
            {
                Console.WriteLine("!");
                return;
            }
            Console.WriteLine(maxi--);
        } while (!Check(m, n));


        Debug(m, n);
    }

    static void Debug(int[,,] m, int n)
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                Console.Write(m[i, j, 0] + "-" + m[i, j, 1] + " ");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
    }
}

Agora, se você quiser testá-lo com n = 3, terá que esperar uma hora, então aqui está outra versão:

public static void Main()
{
    int n = 3;
    Console.WriteLine(n);
    int maxi = (int)System.Math.Pow((double)n, (double)n*n*2);        
    int[,,] result = new int[n, n, 2];
    Parallel.For(0, n, (I) =>
    {
        int[,,] m = new int[n, n, 2];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
            {
                m[i, j, 0] = I;
                m[i, j, 1] = I;
            }
        while (true)
        {
            m = Next(m, n);
            if (Equals(m, n, I + 1))
            {
                break;
            }
            if (Check(m, n))
            {
                Debug(m, n);
            }
        }
    });
}

Atualização: esqueceu de remover "public".

Atualização: usado "Sistema". em vez de "using System;"; Além disso, graças a Kevin Cruijssen , usou "a" em vez de "args".

Atualização: graças a gastropner e alguém .


argspode ser a:)
Kevin Cruijssen

Cada loop for pode ser transformado de for(X = 0; X < Y; X++)para for(X = Y; X-->0; ), o que deve salvar um byte por loop.
gastropner

11
Você já tentou o Visual C # Interactive Compiler ? Pode salvar bytes. Você também pode enviar uma função anônima. Você também pode atribuir i = 0a definição ie salvar um byte.
meu pronome é monicareinstate

405 bytes com base na sugestão de alguém. É claro que atinge o tempo limite após 60 segundos no TIO, mas economiza bytes usando um lambda e o Compilador Interativo com implícito System. Além disso, if((m[i,j,k]=(m[i,j,k]+ 1) % n)!=0)pode ser if((m[i,j,k]=-~m[i,j,k]%n)>0).
Kevin Cruijssen 04/06

@ Kevin Eu realmente não sinto vontade de ler esse código tentando jogar golfe. Tem certeza de que a parte de impressão funciona corretamente? Parece que ele deve usar Writeou pode salvar bytes adicionando \nà cadeia de caracteres dentro da chamada ou está quebrado. Eu acho que você também pode retornar uma matriz diretamente.
meu pronome é monicareinstate

1

Oitava , 182 bytes

Método de força bruta, o TIO continua expirando e eu tive que executá-lo várias vezes para obter saída para n = 3, mas teoricamente isso deve ser bom. Em vez de pares como (1,2), gera uma matriz de conjugados complexos como 1 + 2i. Isso pode esticar um pouco a regra, mas, na minha opinião, ela ainda se ajusta aos requisitos de saída. Porém, deve haver uma maneira melhor de executar as duas linhas sob a declaração functino, mas não tenho certeza no momento.

function[c]=f(n)
c=[0,0]
while(numel(c)>length(unique(c))||range([imag(sum(c)),imag(sum(c.')),real(sum(c)),real(sum(c.'))])>0)
a=fix(rand(n,n)*n);b=fix(rand(n,n)*n);c=a+1i*b;
end
end

Experimente online!


0

Wolfram Language (Mathematica) , 123 bytes

P=Permutations
T=Transpose
g:=#&@@Select[T[Intersection[x=P[P@Range@#,{#}],T/@x]~Tuples~2,2<->4],DuplicateFreeQ[Join@@#]&]&

Experimente online!

Eu uso a TwoWayRulenotação Transpose[...,2<->4]para trocar as 2ª e 4ª dimensões de uma matriz; caso contrário, isso é bastante direto.

Ungolfed:

(* get all n-tuples of permutations *)
semiLSqs[n_] := Permutations@Range@n // Permutations[#, {n}] &;

(* Keep only the Latin squares *)
LSqs[n_] := semiLSqs[n] // Intersection[#, Transpose /@ #] &;

isGLSq[a_] := Join @@ a // DeleteDuplicates@# == # &;

(* Generate Graeco-Latin Squares from all pairs of Latin squares *)
GLSqs[n_] := 
  Tuples[LSqs[n], 2] // Transpose[#, 2 <-> 4] & // Select[isGLSq];

0

Python 3 , 271 267 241 bytes

Abordagem de força bruta: gere todas as permutações dos pares até encontrar um quadrado graeco-latino. Muito lento para gerar algo maior que n=3no TIO.

Graças a alexz02 por jogar 26 bytes e ao ceilingcat por jogar 4 bytes.

Experimente online!

from itertools import*
def f(n):
 s=range(n);l=len
 for r in permutations(product(s,s)):
  if all([l({x[0]for x in r[i*n:-~i*n]})*l({x[1]for x in r[i*n:-~i*n]})*l({r[j*n+i][0]for j in s})*l({r[j*n+i][1]for j in s})==n**4for i in s]):return r

Explicação:

from itertools import *  # We will be using itertools.permutations and itertools.product
def f(n):  # Function taking the side length as a parameter
 s = range(n)  # Generate all the numbers from 0 to n-1
 l = len  # Shortcut to compute size of sets
 for r in permutations(product(s, s)):  # Generate all permutations of all pairs (Cartesian product) of those numbers, for each permutation:
  if all([l({x[0] for x in r[i * n : (- ~ i) * n]})  # If the first number is unique in row i ...
        * l({x[1] for x in r[i * n:(- ~ i) * n]})  # ... and the second number is unique in row i ...
        * l({r[j * n + i][0] for j in s})  # ... and the first number is unique in column i ...
        * l({r[j * n + i][1] for j in s})  # ... and the second number is unique in column i ...
        == n ** 4 for i in s]):  # ... in all columns i:
   return r  # Return the square

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.