Genera un quadrato Graeco-Latino


24

disclaimer: non sono a conoscenza di soluzioni non bruteforce

Un quadrato Graeco-Latino è, per due insiemi della stessa lunghezza n , una disposizione n×n di celle, ognuna contenente una coppia unica (attraverso l'intero quadrato) di un elemento del primo insieme e un elemento del secondo insieme, tale che tutti i primi elementi e tutti i secondi elementi delle coppie sono unici nella loro riga e colonna. Gli insiemi più comuni usati sono, come si può immaginare, le prime n lettere degli alfabeti greco e latino.

Ecco una foto di un quadrato greco-latino 4x4:inserisci qui la descrizione dell'immagine

Le piazze greco-latine sono utili quanto suonano (l' articolo di Wikipedia menziona "progettazione di esperimenti, programmazione dei tornei e costruzione di quadrati magici"). Il tuo compito è, dato un numero intero positivo n , di generare un n×n Graeco-Latino .

Ingresso

Un numero intero positivo n>2 ; è garantito che esiste un quadrato Graeco-Latino n×n (cioè n6 ).

Produzione

Un quadrato greco-latino con la lunghezza laterale n come una matrice bidimensionale, una matrice di matrici, una matrice appiattita o emessa direttamente.

Gli appunti

  • Non è necessario utilizzare in modo specifico gli alfabeti greco e latino; ad esempio, è consentito anche l'output di coppie di numeri interi positivi.
  • Se scegli di usare un alfabeto che non può essere esteso arbitrariamente, devi (teoricamente; il tuo codice non deve finire prima della morte per calore dell'universo) per supportare una lunghezza laterale massima di almeno 20.

Questo è , quindi vince il codice più corto!



Dobbiamo produrre un solo quadrato, oppure è possibile visualizzare tutti i possibili quadrati come elenco?
Nick Kennedy,

Risposte:


2

Gelatina ,  21  20 byte

-1 grazie a Nick Kennedy (l'opzione di output flat consente un salvataggio di byte di ż"þ`ẎẎQƑ$Ƈ F€p`Z€QƑƇ )

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

Provalo online! (Troppo lento4negli anni '60 su TIO, ma se sostituiamo il potere cartesiano, con Combinazioniœc, si completerà - anche se 5 certamente non lo farà!)

Come?

Œ!ṗ⁸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)

Ecco un 20 . Inizialmente ho scritto questo indipendentemente dal tuo, ma ho finito con qualcosa di abbastanza simile, e poi ho preso ispirazione dal tuo uso del potere cartesiano al posto di una diade di permutazione, quindi probabilmente è meglio usarlo per migliorare il tuo. Nota che hai scritto male Graeco nella tua spiegazione.
Nick Kennedy,

Grazie Nick, non ho notato che ci era permesso di produrre una versione appiattita.
Jonathan Allan,


3

R , 164 148 byte

-molti byte grazie 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

Provalo online!

Drammaticamente inefficiente: penso che sia persino peggio di altri approcci alla forza bruta. Anche per n=3, probabilmente andrà in timeout su TIO. Ecco una versione alternativa (155 byte) che funziona per n=3circa 1 secondo.

m1nnlg

  1. all(1:n^2%in%(n*l+g-n))n2l × g
  2. sono le gquadrati latini?

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

Un'ultima nota: come spesso nel golf del codice R, ho usato la variabile T, che è inizializzata come TRUE, per guadagnare qualche byte. Ma questo significa che quando avevo bisogno del valore reale TRUEnella definizione di m(parametro replacein sample), dovevo usare 1invece di T. Allo stesso modo, poiché sto ridefinendo !come funzione diversa dalla negazione, ho dovuto usare 1-all(...)invece di !all(...).


2

JavaScript (ES6),  159 147  140 byte

n×n

Questa è una semplice ricerca della forza bruta e quindi molto 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=[])

Provalo online!(con output predefinito)

Commentate

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 ? (Sul mio telefono, quindi non del tutto sicuro che funzioni)
Shaggy

Non penso nemmeno che tu ne abbia bisogno o; puoi semplicemente tornare malla fine per 141
Shaggy

n=5

2

Haskell , 207 143 233 byte

(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]

Provalo online!

OK, penso di averlo finalmente preso questa volta. Funziona bene per n = 5, n = 6 volte fuori su TIO ma penso che potrebbe essere solo perché questo nuovo algoritmo è INCREDIBILMENTE inefficiente e fondamentalmente controlla tutte le possibilità fino a quando non trova quello che funziona. Ora sto eseguendo n = 6 sul mio laptop per vedere se termina con qualche altro tempo.

Grazie ancora a @someone per aver segnalato i bug nelle mie versioni precedenti


1
Non conosco Haskell, ma questo mi sembra un errore quando cambio il "4" nel piè di pagina in 5. Sto invocando questo correttamente?
mio pronome è monicareinstate il

@someone Buona cattura, avrei dovuto testarlo. In realtà non sono sicuro di cosa stia andando storto qui, questo potrebbe richiedere del tempo per il debug
user1472751

1
Penso che questo abbia ancora un bug; quando eseguito per n = 5, la tupla (1,1) appare due volte.
mio pronome è monicareinstate il

@someone Man, questo problema è molto più difficile di quanto pensassi. Non riesco proprio a trovare un modo affidabile per bloccare tutti i vincoli contemporaneamente. Non appena mi concentro l'uno sull'altro, mi sfugge di mano. Ho intenzione di segnare come non competitivi per ora fino a quando non riuscirò a trovare più tempo per lavorarci su. Ci scusiamo per non aver testato così accuratamente come avrei dovuto
user1472751

1

C #, 520 506 494 484 byte

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]+" ");}}

L'algoritmo di findinf a square è molto semplice. È ... forza bruta. Sì, è stupido, ma il codice golf non riguarda la velocità di un programma, giusto?

Il codice prima di renderlo più breve:

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

Ora, se vuoi testarlo con n = 3 dovrai aspettare un'ora, quindi ecco un'altra versione:

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

Aggiornamento: dimenticato di rimuovere "pubblico".

Aggiornamento: usato "Sistema". invece di "usare il sistema;"; Inoltre, grazie a Kevin Cruijssen , ha usato "a" anziché "args".

Aggiornamento: grazie a gastropner e qualcuno .


argspuò essere a:)
Kevin Cruijssen il

Ciascuno per il ciclo potrebbe essere trasformato da for(X = 0; X < Y; X++)in for(X = Y; X-->0; ), il che dovrebbe salvare un byte per ciclo.
Gastropner

1
Hai provato il compilatore interattivo Visual C # ? Può salvare byte. Puoi anche inviare una funzione anonima. È inoltre possibile assegnare i = 0nella definizione dii e salvare un byte.
mio pronome è monicareinstate il

405 byte in base al suggerimento di @ qualcuno. Ovviamente scade dopo 60 secondi su TIO, ma salva byte usando un lambda e il compilatore interattivo con implicitoSystem . Inoltre, if((m[i,j,k]=(m[i,j,k]+ 1) % n)!=0)può essere if((m[i,j,k]=-~m[i,j,k]%n)>0).
Kevin Cruijssen il

@Kevin Non ho proprio voglia di leggere quel codice cercando di giocarci. Sei sicuro che la parte di stampa funzioni correttamente? Sembra che dovrebbe usare Writeo potrebbe salvare byte aggiungendo \nalla stringa all'interno della chiamata o altrimenti rotto. Penso che puoi anche restituire direttamente un array.
mio pronome è monicareinstate il

1

Ottava , 182 byte

Metodo di forza bruta, TIO mantiene il timeout e ho dovuto eseguirlo un sacco di volte per ottenere output per n = 3, ma teoricamente dovrebbe andare bene. Invece di coppie come (1,2) produce una matrice di coniugati complessi come 1 + 2i. Questo potrebbe allungare un po 'la regola, ma secondo me si adatta ai requisiti di output. Tuttavia, deve esserci un modo migliore per eseguire le due righe nella dichiarazione di functino, ma al momento non ne sono sicuro.

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

Provalo online!


0

Wolfram Language (Mathematica) , 123 byte

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

Provalo online!

Uso la TwoWayRulenotazione Transpose[...,2<->4]per scambiare la 2a e la 4a dimensione di un array; altrimenti questo è abbastanza semplice.

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 byte

Approccio a forza bruta: genera tutte le permutazioni delle coppie fino a trovare un quadrato Graeco-Latino. Troppo lento per generare qualcosa di più grande rispetto n=3a TIO.

Grazie ad alexz02 per giocare a golf a 26 byte e a ceilingcat per giocare a golf a 4 byte.

Provalo 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

Spiegazione:

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

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.