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 N
tendrá un tamaño razonable 4 ≤ N ≤ 8
o ( 1 ≤ N ≤ 8
si 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 = 7
de su código cada vez, está bien. En términos de un límite absoluto, su código debería poder resolverse N = 7
en 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 código golf , por lo que gana el código más corto en bytes, con una excepción. Para las entradas N = 2, 3
no 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.
fuente
N
. SinN = 1, 5 or 7
embargo, 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
N = 1
caso: las respuestas que apuntan a la bonificación deberían regresar1
, no la cadena vacía.Respuestas:
Python 3,
275260 Bytes * 0.8 =220208 BytesEnfoque recursivo / de retroceso.
R
es la función recursiva,l
es la columna,w
es el derecho,K
es la siguiente entrada.Elegí ponerlo en una matriz 1d e imprimirlo al final para simplificar los índices.
Versión sin golf:
fuente
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:
3 2 1 0
lugar de donde se lee la fila superior0 1 2 3
.0
(la única solución) para n = 1.Sin más preámbulos:
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
que en Funciton se puede expresar como
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:
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í:
La fórmula para esto es bastante sencilla una vez que lo sabes:
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:
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:
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:
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:
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).
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 yor
los 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:0
(secuencia vacía).Җ
para calcular los bits correspondientes a la fila, columna y diagonal (s) actuales. Cambie por i y luegoor
en tomado .Ӂ
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 ).ɱ
(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í:
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
1
en la celda del medio, y el número tomado se ve más o menos así: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:
¿Qué obtendríamos si sumamos estos dos números juntos?
El resultado es:
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:
Si no es cero, la cuadrícula es imposible y podemos detenernos.
fuente
Haskell, 790 * 0.80 = 632 bytes
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
Nothing
paran=2,3
yJust <result>
paran>=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.
fuente
c=[1..r]
, para que pueda usarlo dentroo
yw
. b)minimumBy(\(a,_)(b,_)->compare a b)[...]
eshead$sortOn fst[...]
. c) elv
env=g0!s
só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) reemplazarTrue
con1<2
yFalse
con2<1
. f)and[case g0!s of{[_]->True;_->False}|s<-u]
esall((==1).length.(g0!))u
.(&)m k=
se pueden definir infija:m&k=
. h)not(d
elem(g0!p))
esnotElem d$g0!p
. i)concat(...)
esid=<<(...)
. j) use un operador infijo parah
, por ejemploas%bs=
.``like`this``
!Pyth, 41 bytes
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: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.
fuente
SWI-Prolog, 326 * 0.80 = 260.8 bytes
Editar: guardado 16 bytes gracias a @Mat
Uso
Llame
a(5).
a su intérprete paraN=5
. Esto vuelvefalse
porN=2
oN=3
.Como usa la biblioteca CLPFD, esto no es fuerza bruta pura. Este programa puede encontrar una solución
N=20
en 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.
fuente
maplist(maplist(all_distinct), [R,C,D,E])
[R,C,[D,E]]
embargo, necesito usar , porqueE
yD
son listas simples.N=20
CJam, 87 bytes - 20% de bonificación = 69.6 bytes
Codifica las respuestas. Contiene algunos no imprimibles. Trabajos para a
N = 1
travésN = 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 inferior0 1 2 ... N-1
. ParaN < 4
, la matriz 2D resultante no tiene sentido.`1LL]
crea una lista[N, pretty output, 1, "", ""]
. Luego,(4e<=
saca el primer elemento de esta listaN
y recupera elmin(N, 4) % 4
elemento th del resto. PuesN >= 4
esa es la salida, y de lo contrario son las salidas de casos especiales para los primeros tres casos.Probar aquí .
fuente
C ++,
672 * 0.80645 * 0.80 = 516 bytesPrué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 código de golf , 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.
fuente
if (x[r][p]) return f(c+1,r);
. Estoy trabajando en acortarlo.Clojure,
(215 + 258) * 0.8 = 378.4(174 + 255) * 0.8 = 343.2Dividido en dos partes: conteo de errores
S
y 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).Los puntos de referencia de un solo núcleo (en milisegundos) para 4, 5, 6 y 7 se ejecutan 3 veces:
Original:
Desearía que
S
fuera 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
2
con2
, 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):
Multiproceso con
pmap
:fuente