生成Graeco-Latin正方形


24

免责声明:我不知道任何非蛮力解决方案

对于两组相同长度的Graeco-Latin正方形,是排列,每个像元包含第一组元素和第二组元素的唯一(在整个正方形上)对,这样,该对中的所有第一元素和所有第二元素在其行和列中都是唯一的。就像人们可能猜到的那样,最常用的集合是希腊字母和拉丁字母的前个字母。nn×nn

这是4x4 Graeco-Latin正方形的图片:在此处输入图片说明

Graeco-Latin方块听起来很有用(Wikipedia文章提到“实验设计,比赛安排和构造魔术方块”)。给定正整数,您的任务是生成 Graeco-Latin平方。nn×n

输入项

正整数 ; 确保存在一个 Graeco-Latin平方(即)。n>2n×nn6

输出量

侧面长度为n的Graeco-Latin正方形,它是二维数组,数组数组,展平数组或直接输出。

笔记

  • 您不必专门使用希腊字母和拉丁字母;例如,也允许输出正整数对。
  • 如果您选择使用不能任意扩展的字母,则必须(理论上;您的代码不必在宇宙热死之前完成)支持至少20的最​​大边长。

这是,因此最短的代码获胜!



我们必须输出一个正方形,还是可以将所有可能的正方形作为列表输出?
尼克·肯尼迪

Answers:


2

果冻 21  20 字节

-1多亏了尼克·肯尼迪(平面输出选项允许ż"þ`ẎẎQƑ$Ƈ 保存一个字节F€p`Z€QƑƇ

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

在线尝试!4在TIO上在60秒钟内太慢了,但是如果我们用组合代替笛卡儿幂œc则它将完成 -尽管5当然不会!)

怎么样?

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

这是20点。我最初是独立于您的而写的,但最终得到了类似的东西,然后从您使用笛卡尔力量代替置换双子获得了一些启发,因此最好使用它来改进您的。请注意,您在解释中拼写了Graeco。
尼克·肯尼迪

谢谢尼克,我没注意到我们被允许输出扁平化的版本。
乔纳森·艾伦


3

[R 164个 148字节的

-非常感谢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

在线尝试!

效率极低-我认为这比其他蛮力方法还要糟糕。即使对于n=3,它可能也会在TIO上超时。是一个替代版本(155字节),其运行时间n=3约为1秒。

m1nnlg

  1. all(1:n^2%in%(n*l+g-n))n2l × g
  2. lg拉丁方?

!ñlg2^l2ñ+1个-2lt(l)lgsdñ=0n=1个

最后一点:在R代码高尔夫中,我经常使用变量T初始化为TRUE,以获取一些字节。但是,这意味着,当我需要的实际值TRUE中的定义m(参数replacesample),我不得不使用1代替T。同样,由于我!将函数定义为与否定不同的函数,因此必须使用1-all(...)代替!all(...)


2

的JavaScript(ES6), 159 147  140个字节

n×ñ

这是一个简单的蛮力搜索,因此非常慢。

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=[])

在线尝试!(具有预定的输出)

已评论

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?(在我的电话上,因此不能完全确定它是否有效)
毛茸茸的

我也不认为你需要o;您可以m在最后返回141
Shaggy

n=5

2

Haskell中207个143 233字节

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

在线尝试!

好吧,我想这次我终于明白了。它在n = 5上可以正常工作,n = 6在TIO上超时,但是我认为这可能是因为这种新算法效率极低,并且基本上会检查所有可能性,直到找到可行的方法为止。我现在在笔记本电脑上运行n = 6,看看它是否还会再终止。

再次感谢@someone指出我以前版本中的错误


1
我不知道Haskell,但是当我将页脚中的“ 4”更改为5时,这似乎对我来说是错误的。我是否正确调用了此代码?
我的代词是monicareinstate

@someone不错,我应该已经测试过了。我实际上不确定这里出了什么问题,这可能需要一段时间才能调试
user1472751

1
我认为这仍然有一个错误;当运行n = 5时,元组(1,1)出现两次。
我的代名词是monicareinstate

@someone伙计,这个问题比我想的要难得多。我只是找不到可靠的方法来一次锁定所有约束。一旦我专注于彼此,我就会一发不可收拾。我现在将其标记为不竞争,直到我可以找到更多时间来解决这个问题。抱歉,我没有进行应有的彻底测试
user1472751

1

C#,520个 506 494 484字节

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

查找一个正方形的算法非常简单。这是...蛮力。是的,这很愚蠢,但是代码高尔夫与程序速度无关,对吧?

使代码更短之前的代码:

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

现在,如果要使用n = 3进行测试,则必须等待一个小时,所以这里是另一个版本:

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

更新:忘记删除“公共”。

更新:使用“系统”。而不是“使用系统”;同样,感谢Kevin Cruijssen,使用“ a”代替“ args”。

更新:感谢gastropner某人


args可以是a:)
Kevin Cruijssen

每个for循环可以从转换for(X = 0; X < Y; X++)for(X = Y; X-->0; ),每个循环应节省一个字节。
gastropner

1
您是否尝试过Visual C#交互式编译器?它可以节省字节。您也可以提交匿名功能。您也可以分配i = 0定义i并保存一个字节。
我的代词是monicareinstate

405个字节,基于@someone的建议。当然,在TIO上60秒后它会超时,但是它确实通过使用lambda和带有hidden的Interactive Compiler来节省字节System。也if((m[i,j,k]=(m[i,j,k]+ 1) % n)!=0)可以if((m[i,j,k]=-~m[i,j,k]%n)>0)
凯文·克鲁伊森

@Kevin我真的不喜欢阅读试图打高尔夫球的代码。您确定打印部分工作正常吗?看起来它应该使用Write或可以通过\n在调用内添加到字符串中来节省字节,否则将被破坏。我认为您也可以直接返回数组。
我的代名词是monicareinstate

1

八度,182字节

蛮力法,TIO一直处于超时状态,我不得不运行了很多次才能获得n = 3的输出,但是从理论上讲应该没问题。而不是像(1,2)这样的对,它输出一个复杂共轭矩阵,例如1 + 2i。这可能会稍微延长规则,但我认为它仍然符合输出要求。在functino声明下,必须有一种更好的方法来完成这两行,但是目前我不确定。

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

在线尝试!


0

Wolfram语言(Mathematica),123字节

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

在线尝试!

我使用TwoWayRule符号Transpose[...,2<->4]交换数组的第二维和第四维;否则,这非常简单。

取消高尔夫:

(* 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字节

蛮力方法:生成对的所有排列,直到找到Graeco-Latin正方形。生成任何大于n=3TIO的内容太慢。

由于alexz02为高尔夫26个字节,并ceilingcat为高尔夫4个字节。

在线尝试!

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

说明:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.