用1到N填充NxN网格的行,列和对角线


26

任务

给定输入N,生成并输出一个NxN网格,其中每一行,每一列和两个对角线都包含数字1到N(或0到N-1,如果更容易)。

输入项

输入是一个正整数N。它代表网格中的列数和行数。对于这个问题,您可以假设N将是一个合理的大小,4 ≤ N ≤ 8或者(1 ≤ N ≤ 8如果您获得以下红利)。

输出量

输出将是N× N网格。在网格中,每一行仅包含数字1到N,每列仅包含数字1到N,长度的两个对角线N(一个从(0,0)to (N-1,N-1)到一个从(0,N-1)to到(N-1, 0))仅包含数字1到N。您可以使用数字0到N−1。对于每种N,都有许多可能的解决方案,您只需要打印找到的第一个即可。您无需在数字之间打印空格。

约束条件

您的代码应能够重复产生的结果N >= 7。也就是说,如果您N = 7每次都能实际运行并从代码中获取解决方案,那么您就很好。就绝对限制而言,您的代码N = 7每次运行都应能够在10分钟内解决(即,如果您依赖随机数,则在最坏的情况下,您的代码仍应在10分钟内完成N = 7) 。

例子

  • 输入: 4

    一种可能的输出:

    1 2 3 4
    3 4 1 2
    4 3 2 1
    2 1 4 3
    
  • 输入: 5

    一种可能的输出:

    1 2 3 4 5
    5 3 1 2 4
    2 5 4 3 1
    4 1 2 5 3
    3 4 5 1 2
    
  • 输入: 8

    一种可能的输出:

    1 2 3 4 5 6 7 8
    2 3 1 5 4 8 6 7
    4 1 2 3 7 5 8 6
    6 4 7 8 1 2 3 5
    7 5 8 2 6 3 4 1
    5 8 4 6 2 7 1 3
    8 7 6 1 3 4 5 2
    3 6 5 7 8 1 2 4
    

计分

这是,因此以字节为单位的最短代码获胜,只有一个例外。对于输入N = 2, 3,没有有效的解决方案。如果您的代码可以处理此问题(无需完成任何操作,也不会输出空字符串),并且仍然可以处理N = 1(输出1结果),请减少20%的字节数。


1
相关的,但是由于对角线的要求,这种网格在这里不起作用。
xnor

我喜欢这项挑战,但无法找出连值的演算法N。此JavaScript代码可以N = 1, 5 or 7帮助任何人,但都可以工作:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
user81655 2015年


@steveverrill那不是代码高尔夫。
randomra

1
也许您应该对这种N = 1情况更为明确:针对奖金的答案应该返回1,而不是空字符串。
林恩

Answers:


3

Python 3中,275个 260字节* 0.8 = 220个 208字节

递归/回溯方法。R是递归函数,l是列,w是行,K是下一个条目。

我选择将其放在一维数组中,并在末尾漂亮打印以简化索引。

r=range
n=(int)(input())
def R(A,I):
 l=I%n;w=I//n
 if I==n*n:[print(A[i*n:i*n+n])for i in r(n)];exit()
 for K in r(n):
  if all([all([A[i*n+l]!=K,w!=l or A[i+n*i]!=K,w!=n-1-l or A[n*i+n-i-1]!=K])for i in r(w)]+[A[w*n+i]!=K for i in r(l)]):R(A+[K],I+1)
R([],0)

非高尔夫版本:

def Recurse(A,I,n):
 column=I%n
 row=I//n
 if I==n*n:
     [print(*A[i*n:i*n+n])for i in range(n)] # output
     exit() #end recursion
 for K in range(n):
    # try every possibility. Test if they satisfy the constraints:
    # if so, move the index on. If none of them do, we'll return None.
    # if a child returns None, we'll move onto the next potential child:
    # if all of them fail it will backtrack to the next level.
    if all([
        all([
            A[i*n+column]!=K, # column constraint
            row!=column or A[i+n*i]!=K, # diagonal constraint
            row!=n-1-column or A[n*i+n-i-1]!=K # antidiagonal constraint
            ]) for i in range(row)
        ]+[
            A[row*n+i]!=K for i in range(column) # row constraint
            ]):
        Recurse(A+[K],I+1,n)

Recurse([],0,(int)(input()))

22

功能性,非竞争性

更新!巨大的性能提升!n = 7现在将在10分钟内完成!请参阅底部的说明!

这很有趣。这是用Funciton编写的针对此问题的蛮力解决方案。一些事实:

  • 它在STDIN上接受整数。任何多余的空格都会破坏它,包括在整数后面的换行符。
  • 它使用数字0到n -1(不是1到n)。
  • 它“向后”填充网格,因此您将获得一个解决方案,在该解决方案中,最底行读取3 2 1 0而不是最底行读取0 1 2 3
  • 正确输出0(唯一的解决方案)n = 1。
  • 空输出n = 2和n = 3。
  • 当编译为exe时,大约需要8¼分钟的时间才能得到n = 7(大约在性能提高之前一个小时)。如果不进行编译(使用解释器),则需要大约1.5倍的时间,因此使用编译器是值得的。
  • 作为个人的里程碑,这是我第一次编写整个Funciton程序,而没有首先使用伪代码语言编写程序。我确实是先用实际的C#编写的。
  • (但是,这不是我第一次进行更改以大幅提高Funciton中某些功能的性能。我第一次这样做是在阶乘函数中。由于以下原因,交换乘法操作数的顺序产生了巨大差异乘法算法的工作原理。以防万一您感到好奇。)

无需再费周折:

            ┌────────────────────────────────────┐           ┌─────────────────┐
            │                                  ┌─┴─╖ ╓───╖ ┌─┴─╖   ┌──────┐    │
            │                    ┌─────────────┤ · ╟─╢ Ӂ ╟─┤ · ╟───┤      │    │
            │                    │             ╘═╤═╝ ╙─┬─╜ ╘═╤═╝ ┌─┴─╖    │    │
            │                    │               └─────┴─────┘   │ ♯ ║    │    │
            │                  ┌─┴─╖                             ╘═╤═╝    │    │
            │     ┌────────────┤ · ╟───────────────────────────────┴───┐  │    │
          ┌─┴─╖ ┌─┴─╖   ┌────╖ ╘═╤═╝ ┌──────────┐         ┌────────┐ ┌─┴─╖│    │
          │ ♭ ║ │ × ╟───┤ >> ╟───┴───┘        ┌─┴─╖       │ ┌────╖ └─┤ · ╟┴┐   │
          ╘═╤═╝ ╘═╤═╝   ╘══╤═╝          ┌─────┤ · ╟───────┴─┤ << ╟─┐ ╘═╤═╝ │   │
    ┌───────┴─────┘ ┌────╖ │            │     ╘═╤═╝         ╘══╤═╝ │   │   │   │
    │     ┌─────────┤ >> ╟─┘            │       └───────┐      │   │   │   │   │
    │     │         ╘══╤═╝            ┌─┴─╖   ╔═══╗   ┌─┴─╖ ┌┐ │   │ ┌─┴─╖ │   │
    │     │           ┌┴┐     ┌───────┤ ♫ ║ ┌─╢ 0 ║ ┌─┤ · ╟─┤├─┤   ├─┤ Ӝ ║ │   │
    │     │   ╔═══╗   └┬┘     │       ╘═╤═╝ │ ╚═╤═╝ │ ╘═╤═╝ └┘ │   │ ╘═╤═╝ │   │
    │     │   ║ 1 ╟───┬┘    ┌─┴─╖       └───┘ ┌─┴─╖ │   │      │   │   │ ┌─┴─╖ │
    │     │   ╚═══╝ ┌─┴─╖   │ ɓ ╟─────────────┤ ? ╟─┘   │    ┌─┴─╖ │   ├─┤ · ╟─┴─┐
    │     ├─────────┤ · ╟─┐ ╘═╤═╝             ╘═╤═╝   ┌─┴────┤ + ╟─┘   │ ╘═╤═╝   │
  ┌─┴─╖ ┌─┴─╖       ╘═╤═╝ │ ╔═╧═╕ ╔═══╗ ┌───╖ ┌─┴─╖ ┌─┴─╖    ╘═══╝     │   │     │
┌─┤ · ╟─┤ · ╟───┐     └┐  └─╢   ├─╢ 0 ╟─┤ ⌑ ╟─┤ ? ╟─┤ · ╟──────────────┘   │     │
│ ╘═╤═╝ ╘═╤═╝   └───┐ ┌┴┐   ╚═╤═╛ ╚═╤═╝ ╘═══╝ ╘═╤═╝ ╘═╤═╝                  │     │
│   │   ┌─┴─╖ ┌───╖ │ └┬┘   ┌─┴─╖ ┌─┘           │     │                    │     │
│ ┌─┴───┤ · ╟─┤ Җ ╟─┘  └────┤ ? ╟─┴─┐   ┌─────────────┘                    │     │
│ │     ╘═╤═╝ ╘═╤═╝         ╘═╤═╝   │   │╔════╗╔════╗                      │     │
│ │       │  ┌──┴─╖ ┌┐   ┌┐ ┌─┴─╖ ┌─┴─╖ │║ 10 ║║ 32 ║    ┌─────────────────┘     │
│ │       │  │ << ╟─┤├─┬─┤├─┤ · ╟─┤ · ╟─┘╚══╤═╝╚╤═══╝ ┌──┴──┐                    │
│ │       │  ╘══╤═╝ └┘ │ └┘ ╘═╤═╝ ╘═╤═╝     │ ┌─┴─╖ ┌─┴─╖ ┌─┴─╖                  │
│ │     ┌─┴─╖ ┌─┴─╖  ┌─┴─╖  ┌─┴─╖ ╔═╧═╕     └─┤ ? ╟─┤ · ╟─┤ % ║                  │
│ └─────┤ · ╟─┤ · ╟──┤ Ӂ ╟──┤ ɱ ╟─╢   ├───┐   ╘═╤═╝ ╘═╤═╝ ╘═╤═╝                  │
│       ╘═╤═╝ ╘═╤═╝  ╘═╤═╝  ╘═══╝ ╚═╤═╛ ┌─┴─╖ ┌─┴─╖   │     └────────────────────┘
│         └─────┤      │            └───┤ ‼ ╟─┤ ‼ ║   │        ┌──────┐
│               │      │                ╘═══╝ ╘═╤═╝   │        │ ┌────┴────╖
│               │      │                      ┌─┴─╖   │        │ │ str→int ║
│               │      └──────────────────────┤ · ╟───┴─┐      │ ╘════╤════╝
│               │          ┌─────────╖        ╘═╤═╝     │    ╔═╧═╗ ┌──┴──┐
│               └──────────┤ int→str ╟──────────┘       │    ║   ║ │ ┌───┴───┐
│                          ╘═════════╝                  │    ╚═══╝ │ │ ┌───╖ │
└───────────────────────────────────────────────────────┘          │ └─┤ × ╟─┘
           ┌──────────────┐                                  ╔═══╗ │   ╘═╤═╝
╔════╗     │ ╓───╖ ┌───╖  │                              ┌───╢ 0 ║ │   ┌─┴─╖ ╔═══╗
║ −1 ║     └─╢ Ӝ ╟─┤ × ╟──┴──────┐                       │   ╚═╤═╝ └───┤ Ӂ ╟─╢ 0 ║
╚═╤══╝       ╙───╜ ╘═╤═╝         │                       │   ┌─┴─╖     ╘═╤═╝ ╚═══╝
┌─┴──╖ ┌┐ ┌───╖ ┌┐ ┌─┴──╖ ╔════╗ │                       │ ┌─┤   ╟───────┴───────┐
│ << ╟─┤├─┤ ÷ ╟─┤├─┤ << ║ ║ −1 ║ │                       │ │ └─┬─╜ ┌─┐ ┌─────┐   │
╘═╤══╝ └┘ ╘═╤═╝ └┘ ╘═╤══╝ ╚═╤══╝ │                       │ │   └───┴─┘ │   ┌─┴─╖ │
  │         └─┘      └──────┘    │                       │ └───────────┘ ┌─┤ ? ╟─┘
  └──────────────────────────────┘         ╓───╖         └───────────────┘ ╘═╤═╝
                               ┌───────────╢ Җ ╟────────────┐                │
      ┌────────────────────────┴───┐       ╙───╜            │
      │                          ┌─┴────────────────────┐ ┌─┴─╖
    ┌─┴─╖                      ┌─┴─╖                  ┌─┴─┤ · ╟──────────────────┐
    │ ♯ ║ ┌────────────────────┤ · ╟───────┐          │   ╘═╤═╝                  │
    ╘═╤═╝ │                    ╘═╤═╝       │          │     │              ┌───╖ │
┌─────┴───┘    ┌─────────────────┴─┐   ┌───┴───┐    ┌─┴─╖ ┌─┴─╖          ┌─┤ × ╟─┴─┐
│              │                 ┌─┴─╖ │   ┌───┴────┤ · ╟─┤ · ╟──────────┤ ╘═╤═╝   │
│              │ ┌───╖ ┌───╖  ┌──┤ · ╟─┘ ┌─┴─┐      ╘═╤═╝ ╘═╤═╝        ┌─┴─╖ │     │
│         ┌────┴─┤ ♭ ╟─┤ × ╟──┘  ╘═╤═╝   │ ┌─┴─╖ ┌───╖└┐ ┌──┴─╖      ┌─┤ · ╟─┘     │
│         │      ╘═══╝ ╘═╤═╝ ┌───╖ │     │ │ × ╟─┤ Ӝ ╟─┴─┤ ÷% ╟─┐    │ ╘═╤═╝ ┌───╖ │
│   ┌─────┴───┐     ┌────┴───┤ Ӝ ╟─┴─┐   │ ╘═╤═╝ ╘═╤═╝   ╘══╤═╝ │    │   └───┤ Ӝ ╟─┘
│ ┌─┴─╖ ┌───╖ │     │ ┌────╖ ╘═╤═╝   │   └───┘   ┌─┴─╖      │   │    └────┐  ╘═╤═╝
│ │ × ╟─┤ Ӝ ╟─┘     └─┤ << ╟───┘   ┌─┴─╖ ┌───────┤ · ╟───┐  │ ┌─┴─╖ ┌───╖ │    │
│ ╘═╤═╝ ╘═╤═╝         ╘══╤═╝   ┌───┤ + ║ │       ╘═╤═╝   ├──┴─┤ · ╟─┤ × ╟─┘    │
└───┤     └────┐ ╔═══╗ ┌─┴─╖ ┌─┴─╖ ╘═╤═╝ │ ╔═══╗ ┌─┴─╖ ┌─┴─╖  ╘═╤═╝ ╘═╤═╝      │
  ┌─┴─╖ ┌────╖ │ ║ 0 ╟─┤ ? ╟─┤ = ║  ┌┴┐  │ ║ 0 ╟─┤ ? ╟─┤ = ║    │     │ ┌────╖ │
  │ × ╟─┤ << ╟─┘ ╚═══╝ ╘═╤═╝ ╘═╤═╝  └┬┘  │ ╚═══╝ ╘═╤═╝ ╘═╤═╝    │     └─┤ << ╟─┘
  ╘═╤═╝ ╘═╤══╝ ┌┐     ┌┐ │     │     └───┘       ┌─┴─╖   ├──────┘       ╘═╤══╝
    │     └────┤├──┬──┤├─┘     ├─────────────────┤ · ╟───┘                │
    │          └┘┌─┴─╖└┘       │     ┌┐   ┌┐     ╘═╤═╝ ┌┐   ┌┐            │
    └────────────┤ · ╟─────────┘   ┌─┤├─┬─┤├─┐     └───┤├─┬─┤├────────────┘
                 ╘═╤═╝             │ └┘ │ └┘ │         └┘ │ └┘
                   └───────────────┘    │    └────────────┘

第一版说明

第一个版本解决n = 7 大约需要一个小时。以下内容主要说明了该慢版本的工作原理。在底部,我将解释如何进行更改以使其在10分钟以内。

游览成碎片

该程序需要位。它需要很多位,并且在所有正确的位置都需要它们。经验丰富的Funciton程序员已经知道,如果需要n位,则可以使用以下公式

2 ^ n-1

在Funciton中可以表示为

(1 << n)-1

在进行性能优化时,我想到可以使用以下公式更快地计算相同的值:

¬(-1 << n)

希望您能原谅我没有相应地更新本文中的所有方程式图形。

现在,假设您不需要连续的位;实际上,您希望每k个比特有固定间隔的n个比特,如下所示:

                                 LSB
                                  ↓
00000010000001000000100000010000001
                            └──┬──┘
                               k

一旦知道,此公式就相当简单了:

(((1 << nk)-1)/((1 << k)-1)

在代码中,该函数Ӝ取值nk并计算此公式。

跟踪使用的数字

最终网格中有n个数字,每个数字可以是n个可能的值中的任何一个。为了跟踪哪些号码被允许在每个单元中,我们保持数由Ñ ³位,其中一个位被设置为指示特定值取。最初,该数字显然为0。

该算法从右下角开始。在“猜测”第一个数字为0之后,我们需要跟踪以下事实:沿着相同的行,列和对角线的任何单元格都不再允许使用0:

LSB                               (example n=5)
 ↓
 10000 00000 00000 00000 10000
 00000 10000 00000 00000 10000
 00000 00000 10000 00000 10000
 00000 00000 00000 10000 10000
 10000 10000 10000 10000 10000
                             ↑
                            MSB

为此,我们计算以下四个值:

  • 当前行:我们需要ñ位的每ñ个位(每单元一个),然后将其转移到当前行[R ,记住每行包含ñ ²位:

    (((1 <<n²)−1)/((1 << n)−1)<<n²r

  • 当前列: n²位(每行一个)需要n位,然后将其移到当前列c,记住每列包含n位:

    (((1 <<n³)−1)/((1 <<n²)−1)<<n²r

  • 前向对角线:每个需要n位...(您注意吗?快点,找出答案!)...第nn +1)位(做得好!),但前提是我们确实在前对角线:

    如果(c = r,((1 <<n²(n + 1))− 1)/((1 << n(n + 1))− 1)

  • 向后对角线:这里有两件事。首先,我们如何知道是否在向后的对角线上?在数学上,条件为c =(n -1)-r,与c = n +(- r -1)相同。嘿,这让您想起了什么吗?没错,这是二进制补码,因此我们可以使用按位求反(在Funciton中非常有效)来代替减量。其次,上面的公式假设我们想要设置最低有效位,但是我们不想在后向对角线中设置,因此我们必须将其向上移动...您知道吗?...是的,nn -1)。

    ((1 <<n²(n-1))− 1)/((1 << n(n-1))− 1)<< n(n-1)如果c = n +¬r

    如果n = 1,这也是我们可能除以0的唯一函数。但是,Funciton不在乎。0÷0就是0,不是吗?

在代码中,函数Җ(最下面的一个)采用n和一个索引(从中通过除法和余数计算rc),计算出这四个值并将or它们一起计算。

蛮力算法

蛮力算法由Ӂ(顶部的函数)实现。它采用ñ(网格尺寸),指数(其中在电网,我们目前正在将一个数字),并采取(数量与ñ告诉我们哪些号码³位我们仍然可以发生在每个单元)。

此函数返回一个字符串序列。每个字符串都是网格的完整解决方案。这是一个完整的求解器;如果允许的话,它将返回所有解决方案,但是它将它们作为延迟评估的序列返回。

  • 如果index达到0,则说明我们已成功填充了整个网格,因此我们返回了一个包含空字符串的序列(单个解决方案不覆盖任何单元格)。空字符串是0,我们使用库函数将其转换为单元素序列。

  • 在下面的性能改进下描述的检查在此处进行。

  • 如果index尚未达到0,我们将其减1以得到现在需要放置数字的索引(称为ix)。

    我们用于生成一个包含从0到n − 1 的值的惰性序列。

    然后,我们将ɓ(monadic bind)与一个lambda一起使用,该lambda按顺序执行以下操作:

    • 在相关位首先看决定的数量是否是有效的在不在。我们可以把一些当且仅当拍摄&(1 <<(Ñ × IX)<< )尚未设定。如果已设置,则返回0(空序列)。
    • 使用Җ,以计算对应于当前行,列和对角线(S)的位。按i移,然后or移到take上
    • 递归调用Ӂ以检索剩余单元的所有解决方案,将新的采用并递减的ix传递给它。这将返回不完整的字符串序列;每个字符串都有ix个字符(网格填充到索引ix)。
    • 使用ɱ(映射)浏览由此找到的解决方案,并将i连接到每个解决方案的末尾。如果indexn的倍数,则添加换行符,否则为空格。

产生结果

主程序调用Ӂ(蛮干柜子)与ñ指数 = ñ ²(还记得我们填电网向后),并采取 = 0(最初采取任何操作)。如果结果是空序列(未找到解决方案),则输出空字符串。否则,输出序列中的第一个字符串。请注意,这意味着它将仅评估序列的第一个元素,这就是为什么求解器在找到所有解之前不会继续的原因。

性能改进

(对于那些已经阅读了旧版本说明的人:该程序不再生成需要单独转换为字符串以供输出的序列序列;它只是直接生成一个字符串序列。我已经相应地编辑了说明。但这并不是主要的改进。来了。)

在我的计算机上,第一个版本的已编译exe几乎花了1个小时才能解决n =7。这不在给定的10分钟期限内,所以我没有休息。(嗯,实际上,我之所以不休息是因为我对如何大幅度提高它有这个想法。)

如上所述的算法,每遇到一个设置了所取编号中的所有比特的单元,就会停止搜索并回溯,这表明没有任何内容可放入该单元中。

但是,该算法将继续徒劳地将网格填充到设置了所有那些位的单元为止。如果它可以在任何尚未填充的单元已经设置好所有位后立即停止运行,它将更快,这表明无论我们输入什么数字,我们都无法解决网格的其余部分它。但是,如何在不经过所有单元的情况下有效地检查是否有任何单元设置了n位?

窍门首先是将每个单元格的单个位加到所数字上。而不是上面显示的内容,现在看起来像这样:

LSB                               (example n=5)
 ↓
 10000 0 00000 0 00000 0 00000 0 10000 0
 00000 0 10000 0 00000 0 00000 0 10000 0
 00000 0 00000 0 10000 0 00000 0 10000 0
 00000 0 00000 0 00000 0 10000 0 10000 0
 10000 0 10000 0 10000 0 10000 0 10000 0
                                       ↑
                                      MSB

代替Ñ ³,现在有ñ ²(Ñ在此号码+ 1)位。相应地更改了填充当前行/列/对角线的功能(实际上,完全是重写为诚实的)。不过,该函数仍然每个单元仅填充n位,因此我们刚刚添加的额外位始终为0

现在,假设我们已经完成了计算的一半,我们只是1在中间单元格中放置了一个,所取的数字看起来像这样:

                 current
LSB              column           (example n=5)
 ↓                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0
 00011 0 11110 0 01101 0 11101 0 11100 0
 11111 0 11110 0[11101 0]11100 0 11100 0    ← current row
 11111 0 11111 0 11111 0 11111 0 11111 0
 11111 0 11111 0 11111 0 11111 0 11111 0
                                       ↑
                                      MSB

如您所见,现在不再有左上角的单元格(索引0)和左中角的单元格(索引10)。我们如何最有效地确定这一点?

考虑一个数字,其中每个单元的第0位被置位,但仅直到当前索引为止。使用熟悉的公式很容易计算出这个数字:

((1 <<(n + 1)i)-1)/((1 <<(n + 1))-1)

如果将这两个数字加在一起会得到什么?

LSB                                               LSB
 ↓                                                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0           10000 0 10000 0 10000 0 10000 0 10000 0        ╓───╖
 00011 0 11110 0 01101 0 11101 0 11100 0     ║     10000 0 10000 0 10000 0 10000 0 10000 0            ║
 11111 0 11110 0 11101 0 11100 0 11100 0  ═══╬═══  10000 0 10000 0 00000 0 00000 0 00000 0  ═════   ╓─╜
 11111 0 11111 0 11111 0 11111 0 11111 0     ║     00000 0 00000 0 00000 0 00000 0 00000 0  ═════   ╨
 11111 0 11111 0 11111 0 11111 0 11111 0           00000 0 00000 0 00000 0 00000 0 00000 0          o
                                       ↑                                                 ↑
                                      MSB                                               MSB

结果是:

             OMG
              ↓
        00000[1]01010 0 11101 0 00010 0 00011 0
        10011 0 00001 0 11101 0 00011 0 00010 0
═════   00000[1]00001 0 00011 0 11100 0 11100 0
═════   11111 0 11111 0 11111 0 11111 0 11111 0
        11111 0 11111 0 11111 0 11111 0 11111 0

如您所见,加法会溢出到我们加的额外位中,但前提是必须设置该单元的所有位!因此,剩下要做的就是屏蔽掉这些位(与上面的公式相同,但是<< n)并检查结果是否为0:

00000[1]01010 0 11101 0 00010 0 00011 0    ╓╖    00000 1 00000 1 00000 1 00000 1 00000 1         ╓─╖ ╓───╖
10011 0 00001 0 11101 0 00011 0 00010 0   ╓╜╙╖   00000 1 00000 1 00000 1 00000 1 00000 1        ╓╜ ╙╖    ║
00000[1]00001 0 00011 0 11100 0 11100 0   ╙╥╥╜   00000 1 00000 1 00000 0 00000 0 00000 0  ═════ ║   ║  ╓─╜
11111 0 11111 0 11111 0 11111 0 11111 0   ╓╜╙╥╜  00000 0 00000 0 00000 0 00000 0 00000 0  ═════ ╙╖ ╓╜  ╨
11111 0 11111 0 11111 0 11111 0 11111 0   ╙──╨─  00000 0 00000 0 00000 0 00000 0 00000 0         ╙─╜   o

如果不为零,则网格是不可能的,我们可以停止。

  • 屏幕截图显示了 n = 4到7的解决方案和运行时间。

3
真该死。杜德,真令人印象深刻。
Deusovi 2015年

1
我第二次@Deusovi发表评论,谢谢您为此付出了很多努力
hargasinski 2015年

7

Haskell,790 * 0.80 = 632字节

import Data.List
import Control.Monad
import Data.Array
s r=let{h as bs=[(a,b)|a<-as,b<-bs];(&)m k=(\(Just x)->x)$lookup k m;j=Just;n=Nothing;c=[1..r];q=delete;u=h[1..r]c;o=[(s,[u |u<-[h[1..r][c]|c<-c]++[h[r]c|r<-[1..r]]++[zip[1..r][1..r],zip[1..r][r,r-1..1]],s`elem`u])|s<-u];k=foldr(>=>)j;a p d g0=k[t p d2|d2<-q d(g0!p)]g0;t p d g0|not(d`elem`(g0!p))=j g0|[]<-v=n|[d2]<-v=k[t s2 d2|s2<-[(s,delete s$nub(concat(o&s)))|s<-u]&p]g1|True=k[l[s|s<-u,not(d`elem`v)]|u<-o&p]g1 where{v=q d(g0!p);g1=g0//[(p,v)];l[]_=n;l[d3]g=a d3 d g;l _ r=j r};w g0|and[case g0!s of{[_]->True;_->False}|s<-u]=j g0|True=msum[a s' d g0>>=w|d<-g0!s']where(_,s')=minimumBy(\(a,_)(b,_)->compare a b)[(l,s)|s<-u,let v=g0!s;l=length v,l>1]}in fmap(fmap(\[x]->x))$w$array((1,1),(r,r))[((i,j),[1..r])|i<-[1..r],j<-[1..r]]

我注意到这个问题与数独非常相似。我记得一个老的数独解算我在Haskell写基于此另一个 Python编写的。这是我的第一个高尔夫练习代码或尝试。

这实现了奖励,因为它返回Nothingfor n=2,3Just <result>for n>=4,其中<result>是2D整数值数组。

请参阅此处以获取在线口译员。该代码实际上比帖子中的代码长,因为在线解释器对组成完整程序的要求更为严格(规则说提交可以是功能)。该提交将输入作为函数参数。


1
一些快速提示:a)您定义的内容c=[1..r],因此您可以在o和中使用它w。b)minimumBy(\(a,_)(b,_)->compare a b)[...]head$sortOn fst[...]。c)vin v=g0!s仅使用一次,因此根本不要定义它:l=length$g0!s。d)您仍有两个字母参数名称。E)替代True1<2False2<1。f)and[case g0!s of{[_]->True;_->False}|s<-u]all((==1).length.(g0!))u
nimi 2015年

快速提示,第二部分:g)(&)m k=可以定义为:m&k=。h)not(delem (g0!p))notElem d$g0!p。我)concat(...)id=<<(...)。j)的使用中缀运算符h,如as%bs=
nimi

3
快速的元提示:您可以使用双反引号来正确分隔其中带有反引号的代码​``like`this``​
林恩

4

Pyth,41个字节

#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K
#                                      ;   # while(True)
 Km.SQQ                                    # K = random QxQ 2d list
       I                               ;   # if ...
        .A                                 # all of...
          m                          Q     # map(range(Q))...
                +                          # concat
                 .TK                       # transpose K
                    .Tm,@@Kdd@@Kt-Qdd      # diagonals of K
                      m             d      # map(range(d))
                       ,                   # 2-elem list of...
                        @@Kdd              # K[n][n]
                             @@Kt-Qd       # and K[len(K)-n-1][n]
                    .T                     # transpose
           qQl{d                           # subarrays have no dups...
                                      B;   # ... then, break
                                        K  # output final result

蛮力!

由于这基本上会一直尝试随机洗牌,直到它起作用为止(好吧,它一直在尝试n * [shuffle(range(n))]),因此这确实需要很长时间。以下是一些测试运行,可让您了解所需的时间:

llama@llama:~$ time echo 4 | pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')               
[[2, 1, 0, 3], [0, 3, 2, 1], [3, 0, 1, 2], [1, 2, 3, 0]]
echo 4  0.00s user 0.00s system 0% cpu 0.001 total
pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')  0.38s user 0.00s system 96% cpu 0.397 total

那只是4x4,而且运行时间不到半秒。我实际上是在作弊,因为这是少数几次试验中最好的-大多数试验都花费一到两秒钟。

我还没有在5x5上获得时间(它运行一次就完成了,但是那是在REPL中,我没有在时间上)。

请注意,仅在发布此答案后才将时间限制规则编辑到问题中。


我想这不能在十分钟之内完成7x7吗?^^
Lynn

@Mauris好吧,有时候可以...;)我错过了这个要求吗?我没有看到任何提及时间限制的问题。
门把手

我在评论中看到了它(不是12个小时前的评论)
edc65

抱歉,直到有人提到它我才想到,我现在将编辑挑战
hargasinski 2015年

1
+1为您的评论版本中的抽象ASCII艺术。:)
Ilmari Karonen

3

SWI-Prolog,326 * 0.80 = 260.8字节

:-use_module(library(clpfd)).
a(N):-l(N,R),m(l(N),R),append(R,V),V ins 1..N,transpose(R,C),d(0,R,D),maplist(reverse,R,S),d(0,S,E),m(m(all_distinct),[R,C,[D,E]]),m(label,R),m(p,R).
l(L,M):-length(M,L).
d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)).
p([H|T]):-write(H),T=[],nl;write(' '),p(T).
m(A,B):-maplist(A,B).

编辑:由于@Mat,节省了16个字节

用法

致电a(5).您的翻译N=5。这将返回falseN=2N=3

由于它使用CLPFD库,因此它不是纯暴力破解。该程序可以N=20在我的计算机上大约15秒钟内找到解决方案。

取消+说明:

这基本上像Sudoku求解器一样工作,除了将块约束替换为对角线约束。

:-use_module(library(clpfd)).      % Import Constraints library

a(N):-
    l(N,R),                        % R is a list of length N
    maplist(l(N),R),               % R contains sublists, each of length N
    append(R,V),                   
    V ins 1..N,                    % Each value in the matrix is between 1 and N
    maplist(all_distinct,R),       % Values must be different on each row
    transpose(R,C),
    maplist(all_distinct,C),       % Values must be different on each column
    d(0,R,D),
    maplist(reverse,R,S),          
    d(0,S,E),
    all_distinct(D),               % Values must be different on the diagonal
    all_distinct(E),               % Values must be different on the "anti"-diagonal
    maplist(label,R),              % Affects actual values to each element
    maplist(p,R).                  % Prints each row

l(L,M):-length(M,L).               % True if L is the length of M

d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)). % True if the third argument is the diagonal of the second argument

p([H|T]):-write(H),T=[],nl;write(' '),p(T).  % Prints a row separated by spaces and followed by a new line

非常好!:您可以保存一个字节,maplist(maplist(all_distinct), [R,C,D,E])

1
@mat感谢您的建议,节省了16个字节。我需要使用[R,C,[D,E]],因为ED是简单的列表。
致命

对,非常好的解决方法!

2
@Fatalize告诉您,您的解决方案给人留下了最深刻的印象,因为它是唯一可以解决的方案N=20
hargasinski 2015年

1
@Zequ谢谢!但这主要归功于Prolog令人惊叹的CLPFD库,该库在诸如此类的问题上
做了大量工作

2

CJam,87个字节-20%的奖励= 69.6个字节

qi__"@I/l
ŤˏūȻ
܀ᅀ൹৽჈͚
㑢鴑慚菥洠㬝᚜
"N/=:i0+\,m!f=`1LL](4e<=

硬编码答案。包含一些不可打印的内容。适用于N = 1通过N = 8

基本上,该神秘字符串中的每一行都包含索引为的排列列表,这些排列range(N)以Unicode字符编码。

=:i0+\,m!f=将索引添加到排列列表中,首先在索引列表的末尾添加0,代表底部的行0 1 2 ... N-1。对于N < 4,生成的2D数组是无意义的。

`1LL]创建一个列表[N, pretty output, 1, "", ""]。然后,(4e<=从列表中弹出第一个元素N,并min(N, 4) % 4从其余元素中检索第th个元素。对于N >= 4,这是输出,否则是前三种情况的特殊情况输出。

在这里尝试


0

C ++,672 * 0.80 645 * 0.80 = 516字节

#include <iostream>
int **b,**x,**y,*d,*a,n;
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];
int f(int c,int r) {int i=0,p=c-1;if(r>=n)return 1;if(c == n + 1)return f(1,r+1);R(i)int m=r==i,s=r+i==n-1;if(!b[r][i]&&!x[r][p]&&!(m&&d[p])&&!(s&&a[p])&&!y[i][p]){b[r][i]=c;x[r][p]=1;y[i][p]=1;if(m)d[p]=1;if(s)a[p]=1;if(f(c+1,r))return 1;b[r][i]=0;x[r][p]=0;y[i][p]=0;if(m)d[p]=0;if(s)a[p]=0;}}return 0;}
int main(){std::cin>>n;int i=0,j=0;b=new int*[n];x=new int*[n];y=new int*[n];E(d);E(a);R(i)E(b[i]);E(x[i]);E(y[i]); d[i]=0;a[i]=0;R(j)b[i][j]=0;x[i][j]=0;y[i][j]=0;}}if(f(1,0)){R(i)R(j)std::cout<<b[i][j];}std::cout<<std::endl;}}}

在这里在线尝试

由于已经发布了几个答案,因此我想我应该发布用于生成示例输出的代码的高尔夫球版。这是我第一次回答,因此欢迎您提供所有反馈。:)

取消+说明:

从本质上讲,代码是强制解决方案。它从第一行开始,从0开始。从第一点开始,如果该点通过所有检查,它将移至下一个数字。如果填满该行,它将移至下一行。如果已完成所有行,则意味着已找到解决方案。如果该点未通过所有检查,则移至下一个点。如果完成了该行,那么它将回溯,因为前一行之一中的数字使解决方案无法实现。

#include <iostream>

// global variables to save bytes on passing these are function arguments
int **b, // this will store the state of the board
    **x, // if x[i][j] is true, row i of b contains the number j
    **y, // if y[i][j] is true, column i of b contains the number j
    *d,  // if d[i] the main diagonal of b contains i
    *a,  // if a[i] the antidiagonal of a contains i
    n;

// preprocessor to save bytes on repeated statements
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];

// Recursively looks for a solution 
// c - the current number to insert in row r
// r - the current row to fill
int f (int c, int r) {
        int i=0,p=c-1;
        if (r >= n) return 1;             // we are done
        if (c == n + 1) return f(1,r+1);  // this row is full, move to the next row
        R(i)                              // go through the positions in this row,
                                                                            // trying to fill them with c
                int m=r==i, s=r+i==n-1;   // check if this position (r,i) is on ones
                                                                            // of the diagonals
                // if this spot isn't filled, and the row (r), column (i) and diagonals
                // (if it's on the diagonal) doesn't contain the number, fill the spot
                if (!b[r][i] && !x[r][p] && !(m&&d[p]) && !(s&&a[p]) && !y[i][p]) {
                        // fill the spot, and indicate that this row, column and diagonals 
                        // contain this number, c
                        b[r][i]=c; x[r][p]=1; y[i][p]=1;
                        if (m) d[p]=1; if (s)a[p]=1;

                        // move onto to the next number, if you find a solution, stop
                        if (f(c+1,r)) return 1;

                        // with this number in this spot, a solution is impossible, clear
                        // its, and clear the checks
                        b[r][i]=0; x[r][p]=0; y[i][p]=0;
                        if (m) d[p]=0; if (s) a[p]=0;
                }
        }

        return 0; // a solution wasn't found
}

int main() {
        std::cin >> n; // get n from STDIN

        // initialization 
        int i=0,j=0;
        b=new int*[n]; x=new int*[n]; y=new int*[n];
        E(d); E(a);
        R(i)
                E(b[i]); E(x[i]); E(y[i]); // initialization the inner arrays of b, x, y
                d[i]=0; a[i]=0;

                R(j)
                        b[i][j]=0; x[i][j]=0; y[i][j]=0; // ensure each point is initialized as 0
                }
        }

        // find a solution starting at the top-left corner and print it if it finds one
        if (f(1,0)) {
                R(i)
                        R(j)
                                std::cout<<b[i][j];
                        }
                        std::cout<<std::endl;
                }
        }
}

重新阅读代码后,我意识到某些检查可能不是必需的,例如if (x[r][p]) return f(c+1,r);。我正在努力缩短它。
hargasinski 2015年

0

Clojure,(215 + 258)* 0.8 = 378.4(174 + 255)* 0.8 = 343.2

分为两部分:错误计数S和匿名函数,该函数通过Tabu搜索进行实际优化。

更新:较短S(在组内计数不同的值),最佳起始状态较差(无随机播放)。

(defn S[I n](count(mapcat set(vals(apply merge-with concat(flatten(for[R[(range n)]i R j R v[[(I(+(* n i)j))]]][{[1 i]v}{[2 j]v}(if(= i j){3 v})(if(=(- n 1 i)j){4 v})])))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(for[R[(range %)]i R j R]i))P #{}](let[[s I](last(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(=(*(+(* % 2)2)%)s)(partition % I)(recur I(conj P I))))))

4、5、6和7的单核基准测试(以毫秒为单位)运行3次:

[[  131.855337   132.96267    138.745981]
 [ 1069.187325  1071.189488  1077.339372]
 [ 9114.736987  9206.65368   9322.656693]
 [36546.309408 36836.567267 36928.346312]]

原版的:

(defn S[I n](apply +(flatten(for[p(concat(partition n I)(for[p(apply map vector(partition n(range(count I))))](map I p))[(take-nth(inc n)I)][(rest(butlast(take-nth(dec n)I)))])](remove #{1}(vals(frequencies p)))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(flatten(map shuffle(repeat %(range %)))))P #{}](let[[s I](first(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(= s 0)(partition % I)(recur I(conj P I))))))

我希望S它更短一些,但是因为它只计算一个以上/分区的出现,所以停止标准很简单(= s 0)

许多CPU周期浪费在无用的交换上,例如,如果2与交换,它不会提高得分2,并且您不必在行之间交换数字,因为它们都以不同的值开头。

Intel 6700K的基准(以毫秒为单位):

(defn S[I n]( ... )
(def F #( ... ))

(defmacro mytime[expr]
  `(let [start# (. System (nanoTime)) ret# ~expr]
     (/ (double (- (. System (nanoTime)) start#)) 1000000.0)))

(pprint(vec(for[n[4 5 6 7]](vec(sort(repeatedly 5 #(mytime (F n)))))))

[[  43.445902   45.895107   47.277399   57.681634    62.594037]
 [ 222.964582  225.467034  240.532683  330.237721   593.686911]
 [2285.417473 2531.331068 3002.597908 6361.591714  8331.809410]
 [3569.62372  4779.062486 5725.905756 7444.941763 14120.796615]]

多线程与pmap

[[   8.881905  16.343714   18.87262  18.9717890   22.194430]
 [  90.963870 109.719332  163.00299  245.824443  385.365561]
 [ 355.872233 356.439256 1534.31059 2593.482767 3664.221550]
 [1307.727115 1554.00260 2068.35932 3626.878526 4029.543011]]
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.