Rellene las filas, columnas y diagonales de una cuadrícula NxN con 1 a N


26

Tarea

Dada la entrada N, genera y genera una cuadrícula NxN donde cada fila, columna y las dos diagonales contienen los números 1 a N(o 0 a N-1 si eso es más fácil).

Entrada

La entrada es un entero positivo N. Representa el número de columnas y filas en la cuadrícula. Para este problema, puede suponer que Ntendrá un tamaño razonable 4 ≤ N ≤ 8o ( 1 ≤ N ≤ 8si elige la bonificación a continuación).

Salida

La salida será la cuadrícula N× N. En la cuadrícula, cada fila solo contiene los números del 1 al N, cada columna solo contiene los números del 1 al N, y las dos diagonales de longitud N(una de (0,0)a (N-1,N-1)y la de (0,N-1)a (N-1, 0)) solo contienen los números del 1 al N. Puedes usar los números del 0 al N−1. Para cada una N, hay muchas soluciones posibles, solo necesita imprimir la primera que encuentre. No necesita imprimir espacios entre los números.

Restricciones

Su código debería ser capaz de producir resultados repetidamente para N >= 7. Es decir, si puede ejecutar y obtener una solución N = 7de su código cada vez, está bien. En términos de un límite absoluto, su código debería poder resolverse N = 7en menos de 10 minutos cada vez que lo ejecute (es decir, si depende de números aleatorios, en el peor de los casos, su código aún debería terminar en menos de 10 minutos N = 7) .

Ejemplos

  • Entrada: 4

    Una salida posible:

    1 2 3 4
    3 4 1 2
    4 3 2 1
    2 1 4 3
    
  • Entrada: 5

    Una salida posible:

    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
    
  • Entrada: 8

    Una salida posible:

    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
    

Tanteo

Este es el , por lo que gana el código más corto en bytes, con una excepción. Para las entradas N = 2, 3no hay soluciones válidas. Si su código puede manejar esto (ejecutar hasta completar sin generar nada para estos casos, o generar una cadena vacía), y aún maneja N = 1(generar resultados 1), obtenga un 20% de descuento en su conteo de bytes.


1
Relacionado , pero esa cuadrícula no funcionará aquí debido al requisito de diagonales.
xnor

Me gusta este desafío, pero no puedo encontrar un algoritmo para valores pares de N. Sin N = 1, 5 or 7embargo, este código JavaScript funciona si ayuda a alguien:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
user81655

Muy relacionado, posible duplicado: codegolf.stackexchange.com/q/47846/15599
Level River St

@steveverrill Sin embargo, ese no era el código golf.
randomra

1
Tal vez debería ser más explícito sobre el N = 1caso: las respuestas que apuntan a la bonificación deberían regresar 1, no la cadena vacía.
Lynn

Respuestas:


3

Python 3, 275 260 Bytes * 0.8 = 220 208 Bytes

Enfoque recursivo / de retroceso. Res la función recursiva, les la columna, wes el derecho, Kes la siguiente entrada.

Elegí ponerlo en una matriz 1d e imprimirlo al final para simplificar los índices.

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)

Versión sin golf:

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

Funciton , no competitivo

¡ACTUALIZAR! ¡Mejora masiva del rendimiento! ¡n = 7 ahora se completa en menos de 10 minutos! Ver explicación en la parte inferior!

Fue muy divertido escribirlo. Este es un solucionador de fuerza bruta para este problema escrito en Funciton. Algunos factoides:

  • Acepta un número entero en STDIN. Cualquier espacio en blanco extraño lo rompe, incluida una nueva línea después del entero.
  • Utiliza los números 0 a n - 1 (no 1 a n ).
  • Llena la cuadrícula "hacia atrás", por lo que obtienes una solución donde se lee la fila inferior en 3 2 1 0lugar de donde se lee la fila superior 0 1 2 3.
  • Produce correctamente 0(la única solución) para n = 1.
  • Salida vacía para n = 2 yn = 3.
  • Cuando se compila en un exe, toma aproximadamente 8¼ minutos para n = 7 (fue aproximadamente una hora antes de la mejora del rendimiento). Sin compilar (usando el intérprete) toma aproximadamente 1,5 veces más tiempo, por lo que vale la pena usar el compilador.
  • Como un hito personal, esta es la primera vez que escribí un programa Funciton completo sin primero escribir el programa en un lenguaje de pseudocódigo. Aunque lo escribí en C # real primero.
  • (Sin embargo, esta no es la primera vez que hago un cambio para mejorar masivamente el rendimiento de algo en Funciton. La primera vez que lo hice fue en la función factorial. Cambiar el orden de los operandos de la multiplicación hizo una gran diferencia debido a cómo funciona el algoritmo de multiplicación . En caso de que tuviera curiosidad.)

Sin más preámbulos:

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

Explicación de la primera versión.

La primera versión tardó aproximadamente una hora en resolver n = 7. Lo siguiente explica principalmente cómo funcionaba esta versión lenta. En la parte inferior, explicaré qué cambio hice para que llegue a menos de 10 minutos.

Una excursión en pedazos

Este programa necesita bits. Necesita muchos bits, y los necesita en todos los lugares correctos. Los programadores experimentados de Funciton ya saben que si necesita n bits, puede usar la fórmula

2 ^ n-1

que en Funciton se puede expresar como

(1 << n) - 1

Al hacer mi optimización de rendimiento, se me ocurrió que puedo calcular el mismo valor mucho más rápido con esta fórmula:

¬ (−1 << n)

Espero que me perdones por no haber actualizado todos los gráficos de ecuaciones en esta publicación en consecuencia.

Ahora, digamos que no quieres un bloque contiguo de bits; de hecho, quieres n bits a intervalos regulares cada k -ésimo bit, así:

                                 LSB
                                  ↓
00000010000001000000100000010000001
                            └──┬──┘
                               k

La fórmula para esto es bastante sencilla una vez que lo sabes:

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

En el código, la función Ӝtoma los valores n y k y calcula esta fórmula.

Realizar un seguimiento de los números usados

Hay n ² números en la cuadrícula final, y cada número puede ser cualquiera de n valores posibles. Para realizar un seguimiento de qué números están permitidos en cada celda, mantenemos un número que consta de n ³ bits, en el que se establece un bit para indicar que se toma un valor particular. Inicialmente este número es 0, obviamente.

El algoritmo comienza en la esquina inferior derecha. Después de "adivinar" el primer número es un 0, debemos hacer un seguimiento del hecho de que el 0 ya no está permitido en ninguna celda a lo largo de la misma fila, columna y diagonal:

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

Para este fin, calculamos los siguientes cuatro valores:

  • Fila actual: Necesitamos n bits de todos los n bits -ésimo (uno por celda), y luego cambiar a la fila actual r , recordando cada fila contiene n ² trozos:

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

  • Columna actual: necesitamos n bits cada n -²-bit (uno por fila), y luego cambiarlo a la columna actual c , recordando que cada columna contiene n bits:

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

  • Diagonal hacia adelante: necesitamos n bits cada ... (¿prestaste atención? ¡Rápido, descúbrelo!) ... n ( n +1) -ésimo bit (¡bien hecho!), Pero solo si realmente estamos en la diagonal delantera:

    ((1 << n² (n + 1)) - 1) / ((1 << n (n + 1)) - 1) si c = r

  • Diagonal hacia atrás: dos cosas aquí. Primero, ¿cómo sabemos si estamos en la diagonal hacia atrás? Matemáticamente, la condición es c = ( n - 1) - r , que es lo mismo que c = n + (- r - 1). Oye, ¿eso te recuerda algo? Así es, es un complemento de dos, por lo que podemos usar la negación bit a bit (muy eficiente en Funciton) en lugar de la disminución. En segundo lugar, la fórmula anterior supone que queremos que se establezca el bit menos significativo, pero en la diagonal hacia atrás no lo hacemos, por lo que tenemos que desplazarlo hacia arriba ... ¿sabes? ... Así es, n ( n - 1).

    ((1 << n² (n-1)) - 1) / ((1 << n (n-1)) - 1) << n (n-1) si c = n + ¬r

    Este es también el único donde potencialmente dividimos entre 0 si n = 1. Sin embargo, a Funciton no le importa. 0 ÷ 0 es solo 0, ¿no lo sabes?

En el código, la función Җ(la inferior) toma ny un índice (a partir del cual calcula r y c por división y resto), calcula estos cuatro valores y orlos une.

El algoritmo de fuerza bruta

El algoritmo de fuerza bruta es implementado por Ӂ(la función en la parte superior). Se necesita n (el tamaño de la cuadrícula), el índice (dónde en la cuadrícula estamos colocando un número actualmente) y se toma (el número con n ³ bits que nos dice qué números todavía podemos colocar en cada celda).

Esta función devuelve una secuencia de cadenas. Cada cadena es una solución completa a la cuadrícula. Es un solucionador completo; devolvería todas las soluciones si lo deja, pero las devuelve como una secuencia de evaluación diferida.

  • Si el índice ha alcanzado 0, hemos completado con éxito toda la cuadrícula, por lo que devolvemos una secuencia que contiene la cadena vacía (una solución única que no cubre ninguna de las celdas). La cadena vacía es 0, y usamos la función de biblioteca para convertirla en una secuencia de un solo elemento.

  • La verificación descrita en la mejora de rendimiento a continuación ocurre aquí.

  • Si el índice aún no ha llegado a 0, lo disminuimos en 1 para obtener el índice en el que ahora necesitamos colocar un número (llame a eso ix ).

    Usamos para generar una secuencia perezosa que contiene los valores de 0 a n - 1.

    Luego usamos ɓ(enlace monádico) con una lambda que hace lo siguiente en orden:

    • Primero mire el bit relevante tomado para decidir si el número es válido aquí o no. Podemos colocar un número i si y solo si se toma & (1 << ( n × ix ) << i ) ya no está configurado. Si está configurado, devuelve 0(secuencia vacía).
    • Se usa Җpara calcular los bits correspondientes a la fila, columna y diagonal (s) actuales. Cambie por i y luego oren tomado .
    • Llama recursivamente Ӂpara recuperar todas las soluciones para las celdas restantes, pasando la nueva tomada y la ix decrementada . Esto devuelve una secuencia de cadenas incompletas; cada cadena tiene caracteres ix (la cuadrícula se rellena hasta el índice ix ).
    • Use ɱ(map) para ver las soluciones encontradas y use para concatenar i al final de cada una. Agregue una nueva línea si el índice es un múltiplo de n , de lo contrario, un espacio.

Generando el resultado

El programa principal llama Ӂ(el forzador bruto) con n , índice = n ² (recuerde que llenamos la cuadrícula al revés) y tomado = 0 (inicialmente no se toma nada). Si el resultado de esto es una secuencia vacía (no se encontró solución), envíe la cadena vacía. De lo contrario, envíe la primera cadena de la secuencia. Tenga en cuenta que esto significa que solo evaluará el primer elemento de la secuencia, por lo que el solucionador no continúa hasta que haya encontrado todas las soluciones.

Mejora del rendimiento

(Para aquellos que ya leyeron la versión anterior de la explicación: el programa ya no genera una secuencia de secuencias que debe convertirse por separado en una cadena para la salida; solo genera una secuencia de cadenas directamente. He editado la explicación en consecuencia Pero esa no fue la mejora principal. Aquí viene.

En mi máquina, el exe compilado de la primera versión tardó casi exactamente 1 hora en resolver n = 7. Esto no estaba dentro del límite de tiempo dado de 10 minutos, por lo que no descansé. (Bueno, en realidad, la razón por la que no descansé fue porque tuve esta idea sobre cómo acelerarlo masivamente).

El algoritmo descrito anteriormente detiene su búsqueda y retrocede cada vez que encuentra una celda en la que se establecen todos los bits en el número tomado , lo que indica que no se puede poner nada en esta celda.

Sin embargo, el algoritmo continuará llenando inútilmente la cuadrícula hasta la celda en la que se establecen todos esos bits. Sería mucho más rápido si pudiera detenerse tan pronto como cualquier celda que aún no se haya completado ya tenga todos los bits establecidos, lo que ya indica que nunca podremos resolver el resto de la cuadrícula sin importar los números que ingresemos eso. Pero, ¿cómo verifica eficientemente si alguna celda tiene sus n bits establecidos sin pasar por todos ellos?

El truco comienza agregando un solo bit por celda al número tomado . En lugar de lo que se mostró arriba, ahora se ve así:

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

En lugar de n ³, ahora hay n ² ( n + 1) bits en este número. La función que llena la fila / columna / diagonal actual se ha cambiado en consecuencia (en realidad, se reescribió por completo para ser sincero). Esa función fijo pueblan sólo n bits por celda sin embargo, por el poco extra que acabamos de añadir siempre será 0.

Ahora, digamos que estamos a la mitad del cálculo, acabamos de colocar un 1en la celda del medio, y el número tomado se ve más o menos así:

                 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

Como puede ver, la celda superior izquierda (índice 0) y la celda central izquierda (índice 10) ahora son imposibles. ¿Cómo determinamos esto más eficientemente?

Considere un número en el que se establece el bit 0 de cada celda, pero solo hasta el índice actual. Tal número es fácil de calcular usando la fórmula familiar:

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

¿Qué obtendríamos si sumamos estos dos números juntos?

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

El resultado es:

             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

Como puede ver, la adición se desborda en el bit adicional que agregamos, ¡pero solo si se establecen todos los bits para esa celda! Por lo tanto, todo lo que queda por hacer es enmascarar esos bits (la misma fórmula que arriba, pero << n ) y verificar si el resultado es 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

Si no es cero, la cuadrícula es imposible y podemos detenernos.


3
SANTA JODIDA. Amigo, eso es impresionante.
Deusovi

1
Apoyo el comentario de @ Deusovi, gracias por poner tanto esfuerzo en esto
hargasinski

7

Haskell, 790 * 0.80 = 632 bytes

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

Noté que este problema es muy similar al sudoku. Recuerdo un viejo solucionador de sudoku que escribí en Haskell basado en este otro en Python. Esta es mi primera publicación o intento de código de golf.

Esto cumple la bonificación porque devuelve Nothingpara n=2,3y Just <result>para n>=4, donde <result>hay una matriz 2D de valores integrales.

Ver aquí para intérprete en línea. Ese código es en realidad más largo que el de la publicación porque el intérprete en línea tiene requisitos más estrictos sobre qué forma un programa completo (las reglas dicen que un envío puede ser una función). Esta presentación toma la entrada como un argumento de función.


1
Algunos consejos rápidos: a) usted define c=[1..r], para que pueda usarlo dentro oy w. b) minimumBy(\(a,_)(b,_)->compare a b)[...]es head$sortOn fst[...]. c) el ven v=g0!ssólo se utiliza una vez, así que no define en absoluto: l=length$g0!s. d) todavía tiene algunos nombres de parámetros de dos letras. e) reemplazar Truecon 1<2y Falsecon 2<1. f) and[case g0!s of{[_]->True;_->False}|s<-u]es all((==1).length.(g0!))u.
nimi

Consejos rápidos, parte II: g) (&)m k=se pueden definir infija: m&k=. h) not(delem (g0!p))es notElem d$g0!p. i) concat(...)es id=<<(...). j) use un operador infijo para h, por ejemplo as%bs=.
nimi

3
Meta sugerencias rápidas: ¡puede delimitar el código que tiene backticks correctamente usando dobles backticks ​``like`this``​!
Lynn

4

Pyth, 41 bytes

#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

Fuerza bruta ftw!

Dado que esto básicamente sigue intentando barajar aleatoriamente hasta que funciona (bueno, sigue intentándolo n * [shuffle(range(n))]), lleva mucho, mucho tiempo. Aquí hay algunas ejecuciones de prueba para darle una idea de cuánto tiempo lleva:

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

Eso es solo 4x4, y se ejecuta en poco menos de medio segundo. De hecho, estoy haciendo trampa porque esta es la mejor de algunas pruebas, la mayoría de ellas toman más de un segundo o dos.

Todavía tengo que obtener un cronometraje en 5x5 (se ejecutó hasta su finalización una vez, pero eso estaba en un REPL y no lo estaba cronometrando).

Tenga en cuenta que la regla para el límite de tiempo solo se editó en la pregunta después de que se publicó esta respuesta.


¿Supongo que esto no puede hacer 7x7 en diez minutos? ^^
Lynn

@Mauris Bueno, a veces puede ...;) ¿Es un requisito que no cumplí ? No veo nada que mencione un límite de tiempo en la pregunta.
Pomo de la puerta

Lo veo en los comentarios, (no es un comentario nuevo , hace 12 horas)
edc65

Lo siento, no lo pensé hasta que alguien lo mencionó,
editaré

1
+1 para el arte abstracto ASCII en su versión comentada. :)
Ilmari Karonen

3

SWI-Prolog, 326 * 0.80 = 260.8 bytes

:-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).

Editar: guardado 16 bytes gracias a @Mat

Uso

Llame a(5).a su intérprete para N=5. Esto vuelve falsepor N=2o N=3.

Como usa la biblioteca CLPFD, esto no es fuerza bruta pura. Este programa puede encontrar una solución N=20en aproximadamente 15 segundos en mi computadora.

Ungolfed + explicaciones:

Básicamente, esto funciona como un solucionador de Sudoku, excepto que las restricciones de bloques se reemplazan con las restricciones diagonales.

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

¡Muy agradable! Puede guardar un bytes con:maplist(maplist(all_distinct), [R,C,D,E])
mat

1
@mat Gracias por la sugerencia, ahorra 16 bytes. Sin [R,C,[D,E]]embargo, necesito usar , porque Ey Dson listas simples.
Fatalize

Correcto, muy buena solución!
mat

2
@Fatalize Solo para hacerle saber, su solución fue la más impresionante, ya que es la única que se resuelveN=20
hargasinski

1
@Zequ ¡Gracias! Pero eso se debe principalmente a la increíble biblioteca CLPFD de Prolog, que hace el trabajo pesado en problemas como estos :)
Fatalize

2

CJam, 87 bytes - 20% de bonificación = 69.6 bytes

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

Codifica las respuestas. Contiene algunos no imprimibles. Trabajos para a N = 1través N = 8.

Básicamente, cada línea en esa cadena misteriosa contiene índices en la lista de permutaciones de range(N), codificados como caracteres Unicode.

=:i0+\,m!f=indexa en la lista de permutaciones, agregando un 0 al final de la lista de índices primero, representando la fila inferior 0 1 2 ... N-1. Para N < 4, la matriz 2D resultante no tiene sentido.

`1LL]crea una lista [N, pretty output, 1, "", ""]. Luego, (4e<=saca el primer elemento de esta lista Ny recupera el min(N, 4) % 4elemento th del resto. Pues N >= 4esa es la salida, y de lo contrario son las salidas de casos especiales para los primeros tres casos.

Probar aquí .


0

C ++, 672 * 0.80 645 * 0.80 = 516 bytes

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

Pruébalo en línea aquí

Como ya se han publicado un par de respuestas, pensé que publicaría la versión de golf del código que utilicé para generar el resultado de los ejemplos. Esta es la primera vez que respondo un , por lo que todos los comentarios son bienvenidos. :)

Ungolfed + explicaciones:

Esencialmente, el código está forzando una solución bruta. Comienza en la primera fila, con 0. Comienza en el primer lugar, si esos puntos pasan todas las comprobaciones, pasa al siguiente número. Si llena la fila, se mueve a la siguiente fila. Si ha hecho todas las filas, eso significa que se ha encontrado una solución. Si el lugar no pasa todas las comprobaciones, pasa al siguiente lugar. Si se hace la fila, retrocede, ya que un número en una de las filas anteriores impide que sea posible una solución.

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

Después de volver a leer el código, me di cuenta de que algunas de las comprobaciones pueden no ser necesarias, como el if (x[r][p]) return f(c+1,r);. Estoy trabajando en acortarlo.
hargasinski

0

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

Dividido en dos partes: conteo de errores Sy una función anónima que realiza la optimización real a través de la búsqueda Tabu .

Actualización: más corto S(cuenta valores distintos dentro de los grupos), estado inicial menos óptimo (sin barajar).

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

Los puntos de referencia de un solo núcleo (en milisegundos) para 4, 5, 6 y 7 se ejecutan 3 veces:

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

Original:

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

Desearía que Sfuera más corto, pero debido a que solo cuenta las ocurrencias de más de una / partición, el criterio de detención es simple (= s 0).

Muchos ciclos de CPU se desperdician los swaps no útiles, por ejemplo, no mejora la puntuación si intercambia 2con 2, y que no es necesario que los números de intercambio entre las filas ya que todos tienen valores distintos para empezar.

Puntos de referencia con Intel 6700K (en milisegundos):

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

Multiproceso con 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]]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.