Maximiza la diferencia al cuadrado


19

Considere una permutación de los valores enteros de 1a N. Por ejemplo, este ejemplo para N = 4:

[1, 3, 4, 2]

Vamos a considerar que esta lista sea cíclico, de tal manera que 1y 2son tratados como adyacente. Una cantidad que podemos calcular para dicha lista es la diferencia al cuadrado total de los valores adyacentes:

(1-3)² + (3-4)² + (4-2)² + (2-1)² = 10

Su tarea es encontrar una permutación que maximice esta cantidad, dado un número entero positivo N. En el caso del N = 4ejemplo anterior, no es óptimo (de hecho, es mínimo). Podemos lograr una diferencia al cuadrado total de 18con la siguiente permutación (así como varias otras):

[1, 4, 2, 3]

Su algoritmo debe ejecutarse en tiempo polinómico (de N). En particular, no puede simplemente calcular la diferencia al cuadrado total de todas las permutaciones.

Puede escribir un programa o función, tomando la entrada a través de STDIN (o la alternativa más cercana), argumento de línea de comando o argumento de función y generando el resultado a través de STDOUT (o la alternativa más cercana), el valor de retorno de la función o el parámetro de función (out).

La salida puede estar en cualquier formato de cadena o lista conveniente, inequívoca y plana. Puede elegir devolver una lista con valores de 0a en N-1lugar de 1a N.

Aplican reglas estándar de .

Datos de prueba

Hay una buena solución analítica para este problema. Por ejemplo, todas las soluciones válidas N = 10son equivalentes a la siguiente lista (hasta cambios cíclicos y reversión):

[7, 5, 6, 4, 8, 2, 10, 1, 9, 3]

No quiero revelar mucho más allá de eso (aunque probablemente sea suficiente para descubrir el patrón), por lo que en lugar de dar más ejemplos, puede verificar que sus resultados tengan las siguientes diferencias cuadradas totales para un determinado N:

N    Total squared difference

1                         0
2                         2
3                         6
4                        18
5                        36
6                        66
7                       106
8                       162
9                       232
10                      322
33                    11936
100                  333202
333                12308236
1000              333332002

Esta es la entrada OEIS A064842 (que también contiene una referencia a un documento con una solución a este desafío si está atascado).

Respuestas:


7

Jalea, 24 21 15 14 10 9 bytes

RUĖµ«/€ị"

Para calcular la diferencia al cuadrado total, agregue µ_ṙ1$²Sel código. Pruébalo en línea!

Antecedentes

Una forma de generar una permutación con una diferencia al cuadrado maximizada es tomar los enteros 1 a n en orden ascendente e intercambiar el segundo desde la izquierda con el segundo desde la derecha, el cuarto desde la izquierda con el cuarto desde la derecha, y así adelante hasta que nos encontremos en el medio.

Por ejemplo, para n = 8, 9 tenemos

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

(las marcas marcan los enteros que se intercambiarán), lo que da como resultado

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

Después de cambiar.

Una forma de lograr estos intercambios, independientemente de la paridad de n , es la siguiente.

Comience escribiendo los enteros en orden ascendente y descendente, uno debajo del otro.

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

Para cada par de enteros, calcule el mínimo del par. Esto proporciona la distancia al borde más cercano, es decir, el índice desde la izquierda o la derecha (el que sea más bajo).

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

1 2 3 4 4 3 2 1        1 2 3 4 5 4 3 2 1

Si el mínimo es impar, el entero debe permanecer en su lugar, por lo que seleccionamos el de la primera fila; si es par, los enteros deben intercambiarse, por lo que seleccionamos el de la segunda fila.

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

Esta es la salida deseada.

Cómo funciona

RUĖµ«/€ị"    Main link. Input: n

R            Range. Yields [1, ..., n].
 U           Upend. Yields [n, ..., 1].
  Ė          Enumerate. Yields p := [[1, n], [2, n-1], ... [n-1, 2], [n, 1]].

   µ         Begin a new, monadic chain. Argument: p
     /       Reduce...
      €        each pair of p...
    «          by minimum.
        "    For each minimum and the corresponding pair of p:
       ị       Select the element at that index.
            Indices are modular and 1-based in Jelly, so this selects the first
            element if the minimum is odd, and the second one if it is even.

6

JavaScript (ES6), 52 bytes

n=>[...Array(n)].map((_,i)=>(i<n/2|n%2)^i%2?i+1:n-i)

¡9 bytes guardados gracias a @Neil!

Explicación

Este enfoque determina el número que debe estar en el índice icon una longitud de en nlugar de concatenar los resultados a una matriz. Esto se basa en la siguiente observación (utilizando n = 7como ejemplo):

  • Comience con el número más bajo a la izquierda y el más alto a la derecha: [ 1, 7 ]
  • Cambie el orden para que el más bajo esté a la derecha y el más alto a la izquierda, incremente el más bajo, disminuya el más alto y colóquelos en el centro de la matriz:[ 1, 6, 2, 7 ]
  • Repita hasta que el más alto y el más bajo converjan: [ 1, 6, 3, 4, 5, 2, 7 ]

Los números más altos y más bajos se pueden expresar fácilmente como n-iy i+1respectivamente.

var solution =

n=>
  [...Array(n)] // create an array of length n
  .map((_,i)=>  // set each value of the array at index i
    (i<n/2      // if we're on the left side,
    |n%2)       // or we're on the right and n is odd, even i => lower, odd i => higher
    ^i%2?       // else even i => higher, odd i => lower
    i+1:n-i
  )
N = <input type="number" id="input" oninput="result.textContent=solution(+this.value)" />
<pre id="result"></pre>


Buen algoritmo; Traté y no pude pensar en una fórmula para generarlos, así que tuve que usar el método más feo de empujar y no cambiar. Sin embargo, por supuesto, puedo simplificar su lógica (i<n/2||n%2)^i%2?i+1:n-i.
Neil

@Neil Wow, me acabo de despertar, decidí jugar al golf y se me ocurrió tu lógica exacta y comencé a escribirlo justo cuando lo publicaste. Crazy ...
user81655

5

Python2, 105 98 bytes

7 bytes guardados gracias al comentario de @Dennis

n=input()
r=([],[n/2+1])[n%2]
for i in range(n/2,0,-1):k=[n+1-i];r=([i]+r+k,k+r+[i])[i%2]
print r

Versión editada 58 bytes

lambda n:[(n-i-1,i)[(i+(n,1)[i<n/2])%2]for i in range(n)]

Ya creía que debería ser posible hacerlo de una sola vez, pero la lógica era demasiado compleja para mí. Al ver la respuesta JavaScript de @ user81655 y la notación lambda en @Dennis Python-answer, le di una nueva oportunidad.

La condición es igual a

if i < n/2:
    i%2 != n%2
else:
    (i+1)%2

Desafortunadamente, todo el esfuerzo de transformación ahorra un solo byte en comparación con la traducción directa (i<n/2or n%2)!=i%2de la lógica de JavaScript.


3
¡Bienvenido a Programming Puzzles & Code Golf! Esto parece ser Python 2, por lo que no necesita int()la entrada. Además, puede colocar el cuerpo del bucle for en la misma línea que for....
Dennis

4

Python, 51 49 bytes

lambda n:[(i^min(i,~i%n)%-2)%n for i in range(n)]

¡Gracias a @xnor por jugar 2 bytes!

Pruébalo en Ideone .

Cómo funciona

Si i es un número en [0, ..., n - 1] , entonces ~ i% n = - (i + 1)% n = - (i + 1) + n = (n - 1) - i , lo que significa que los mapas de 0 a n - 1 , 1 a n - 2 y, en general, el j ésimo elemento de la izquierda a la j ésimo desde la derecha.

Como se explica en mi respuesta de Jelly , podemos construir la salida mirando el valor más bajo entre i y ~ i% n , y elegir i si es par y ~ i% n si es impar. Logramos esto de la siguiente manera.

  • Si el mínimo es par, min(i,~i%n)%-2producirá 0 , entonces XORing el resultado con i producirá i , y calcular su módulo de residuo n devolverá i .

  • Si el mínimo es impar, min(i,~i%n)%-2producirá -1 , por lo que XORing el resultado con i producirá ~ i , por lo que toda la expresión se evalúa a ~ i% n como se desee.


Puede guardar un par de caracteres haciendo el condicional como (i^min(i,n+~i)%-2)%n.
xnor

Eso no solo es corto sino increíblemente inteligente. ¡Gracias!
Dennis

2

PHP, 77 76 51 50 49 bytes

Utiliza la codificación ISO 8859-1.

Ensamblar la primera mitad de la matriz de esta manera:

  • Los números impares tienen su valor de índice (1, 3, 5 ..)
  • Los números pares tienen el valor de N+1-index(9, 7, 5)
  • Esto resulta en 1, 9, 3, 7, 5

En cuanto a la segunda mitad de la matriz, los valores más externos se suman N+1, lo que significa que puede obtener el valor correcto correspondiente de N-[left value]donde ya se conoce el valor izquierdo.

for(;$k=$argv[1]-$j++;)echo" ",min($j,$k)%2?$j:$k;

Ejecutar así (esto también muestra la diferencia al cuadrado total) ( -dagregado solo para la estética):

php -d error_reporting=32757 -r 'for(;$k=$argv[1]-$j++;)echo~ß,$x[]=min($j,$k)%2?$j:$k;  for(;$c=$x[+$i++];)$b+=($c-($x[$i]?:$x[0]))**2;echo"\n$b\n";' 10
  • Guardado un byte al negar la condición izquierda / derecha para que el segundo ternario pueda anidarse sin paréntesis
  • Ahorró 25 bytes al implementar descaradamente el algoritmo de Dennis
  • Salvó un byte al deshacerse del espacio necesario después echo
  • Se guardó un byte al usar para producir un espacio.

1

Pitón 2, 100

Sé que ya hay una respuesta de Python, pero creo que podría haberlo hecho de manera diferente.

n=input();a=n%2;b=n/2;x=[b+1,b+a][a:]
for i in range(b+a-1):f=1-i%2*2;x=[x[-1]-f]+x+[x[0]+f]
print x

Y como extra para probar la puntuación total:

def t(x,n):return sum((x[i]-x[(i+1)%n])**2for i in range(n))

def t(x,n):return sum((x[i]-x[i-1])**2for i in range(n))usa la envoltura implícita de índices negativos y guarda 4 bytes. Lo sé, no era parte de la competencia. ;)
cierto

1

CJam, 17 15 14 bytes

{,W%ee_::e<.=}

Esta es una función que saca un entero n de la pila y empuja una permutación de [0 ... n-1] a cambio. El código utiliza el mismo enfoque que mi respuesta Jelly .

Pruébalo en línea!

Cómo funciona

,W%ee_::e<.=    Function body. Stack: N

,               Turn N into [0 ... N-1].
 W%             Reverse to push [N-1 ... 0].
   ee           Enumerate. This pushes [[0 N-1] [1 N-2] ... [N-2 1] [N-1 0]].
     _          Push a copy of the array of pairs.
      ::e<      Reduce each pair by minimum.
          .=    Vectorized selection.
                For the Ith minimum M, select the Mth element of the Ith pair.
                Indices are modular and 0-based in CJam, so this selects the first
                element if the minimum is even, and the second one if it is odd.

1

LISP, 86 bytes

(defun g(n m)(if(= n m)(list n)(if(< m n)(cons m(reverse(cons n(g(- n 1)(+ m 1))))))))

Las entradas de la función permiten elegir los valores de inicio (m) y final (n) de la secuencia.

Para probar la función de acuerdo con las muestras proporcionadas, n se fija a N ym a 1.

Aquí el código para probar la función:

    (defun g(n m)(if(= n m)(list n)(if(< m n)(cons m(reverse(cons n(g(- n 1)(+ m 1))))))))

(defun sq (c)
    (apply #'+ (mapcar #'(lambda(x y) (* (- x y) (- x y))) c (append (cdr c) (list (car c))))))

(format t "N~20TSequence~50TSquared Difference~%")
(mapcar #'(lambda (x)(format t "~S~20T~S~50T~S~%" x (g x 1) (sq (g x 1)))) '(1 2 3 4 5 6 7 8 9 10 33 100 333 1000))

Pruébalo en Ideone !


1

Julia, 39 bytes

n->map(i->min(i-1,n-i)%2>0?n-~-i:i,1:n)

Esto imprime una permutación de 1: n . Una permutación de 0: n-1 no cuesta ni guarda bytes:

n->map(i->min(i,n+~i)%2>0?i:n+~i,0:n-1)

Esta última versión es un puerto directo de mi respuesta de Python .


0

ES6, 77 bytes

n=>[...Array(n)].map(_=>r[++i&2?"push":"unshift"](i&1?n--:++j),i=j=0,r=[])&&r

Las i&1muestras de los dígitos de los extremos hacia el centro. Los i&2agrega al principio o al final del resultado en pares.


0

R, 117 86 bytes

z=1:(n<-scan());a=pmin(z,n:1);for(i in seq(2,,2,n%/%2))z[b]=z[rev(b<-which(a==i,T))];z

edit reemplazó la versión larga con errores con una implementación del algoritmo Jelly de @Dennis

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.