Maximieren Sie die quadratische Differenz

19

Betrachten Sie eine Permutation der ganzzahligen Werte von 1bis N. ZB dieses Beispiel für N = 4:

[1, 3, 4, 2]

Wir werden diese Liste prüfen sein zyklisch, so dass 1und 2wie benachbarte behandelt. Eine Größe, die wir für eine solche Liste berechnen können, ist die quadratische Gesamtdifferenz benachbarter Werte:

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

Ihre Aufgabe ist es, eine Permutation zu finden, die diese Größe bei einer positiven ganzen Zahl maximiert N. Im Falle des N = 4obigen Beispiels ist es nicht optimal (in der Tat ist es minimal). Wir können eine quadratische Gesamtdifferenz von 18mit der folgenden Permutation (wie auch mit mehreren anderen) erzielen :

[1, 4, 2, 3]

Ihr Algorithmus muss in Polynomialzeit (von N) ausgeführt werden. Insbesondere können Sie nicht einfach die quadratische Gesamtdifferenz aller Permutationen berechnen.

Sie können ein Programm oder eine Funktion schreiben, indem Sie eine Eingabe über STDIN (oder die nächstgelegene Alternative), ein Befehlszeilenargument oder ein Funktionsargument vornehmen und das Ergebnis über STDOUT (oder die nächstgelegene Alternative), einen Funktionsrückgabewert oder einen Funktionsparameter (out) ausgeben.

Die Ausgabe kann in einem beliebigen praktischen, eindeutigen, einfachen Listen- oder Zeichenfolgenformat erfolgen. Sie können eine Liste mit Werten von 0bis N-1anstelle von 1bis zurückgeben N.

Es gelten die Standardregeln für .

Testdaten

Für dieses Problem gibt es eine gute analytische Lösung. ZB entsprechen alle gültigen Lösungen für N = 10die folgende Liste (bis zu zyklischen Verschiebungen und Umkehrungen):

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

Ich möchte nicht zu viel darüber hinaus verraten (obwohl es wahrscheinlich ausreicht, um das Muster herauszufinden). Anstatt weitere Beispiele zu nennen, können Sie überprüfen, ob Ihre Ergebnisse die folgenden quadratischen Gesamtdifferenzen für eine bestimmte Zahl aufweisen 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

Dies ist der OEIS-Eintrag A064842 (der auch einen Verweis auf ein Dokument mit einer Lösung für diese Herausforderung enthält, wenn Sie nicht weiterkommen).

Martin Ender
quelle

Antworten:

7

Jelly, 24 21 15 14 10 9 Bytes

RUĖµ«/€ị"

Hängen Sie µ_ṙ1$²San den Code an, um die quadrierte Gesamtdifferenz zu berechnen . Probieren Sie es online!

Hintergrund

Eine Möglichkeit, eine Permutation mit maximierter quadratischer Differenz zu erzeugen, besteht darin, die ganzen Zahlen 1 bis n in aufsteigender Reihenfolge zu nehmen und die zweite von links mit der zweiten von rechts, die vierte von links mit der vierten von rechts zu tauschen her, bis wir uns in der Mitte treffen.

Zum Beispiel haben wir für n = 8, 9

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

(Carets markieren die auszutauschenden ganzen Zahlen), was zur Folge hat,

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

nach dem tauschen.

Ein Weg, diese Swaps unabhängig von der Parität von n zu erreichen , ist wie folgt.

Beginnen Sie, indem Sie die ganzen Zahlen in aufsteigender und absteigender Reihenfolge untereinander schreiben.

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

Berechnen Sie für jedes Ganzzahlpaar das Minimum des Paares. Dies gibt den Abstand zur nächsten Kante an, dh den Index von links oder rechts (je nachdem, welcher Wert niedriger ist).

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

Wenn das Minimum ungerade ist, sollte die Ganzzahl an ihrer Stelle bleiben, also wählen wir die aus der ersten Zeile aus; Wenn es gerade ist, sollten die ganzen Zahlen vertauscht werden, also wählen wir die aus der zweiten Reihe aus.

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

Dies ist die gewünschte Ausgabe.

Wie es funktioniert

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.
Dennis
quelle
6

JavaScript (ES6), 52 Byte

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

9 Bytes gespart dank @Neil!

Erläuterung

Dieser Ansatz bestimmt die Zahl, die sich am Index befinden soll, imit einer Länge, ndie die Ergebnisse nicht zu einem Array verkettet. Dies basiert auf der folgenden Beobachtung (am n = 7Beispiel):

  • Beginnen Sie mit der niedrigsten Zahl links und der höchsten rechts: [ 1, 7 ]
  • Ändern Sie die Reihenfolge so, dass die niedrigste rechts und die höchste links ist, erhöhen Sie die niedrigste und verringern Sie die höchste und platzieren Sie sie in der Mitte des Arrays:[ 1, 6, 2, 7 ]
  • Wiederholen, bis die höchste und niedrigste Konvergenz erreicht ist: [ 1, 6, 3, 4, 5, 2, 7 ]

Die höheren und niedrigeren Zahlen können leicht ausgedrückt werden n-iund i+1jeweils.

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>

user81655
quelle
Netter Algorithmus; Ich habe versucht, eine Formel zu finden, um sie zu generieren, und musste daher die hässlichere Methode des Drückens und Entladens anwenden. Ich kann aber natürlich deine Logik vereinfachen (i<n/2||n%2)^i%2?i+1:n-i.
Neil
@Neil Wow, ich bin gerade aufgewacht, habe mich dazu entschlossen, Golf zu spielen und habe mir deine genaue Logik ausgedacht. Crazy ...
user81655
5

Python2, 105 98 Bytes

Dank des Kommentars von @Dennis wurden 7 Bytes gespart

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

Bearbeitete Version 58 Bytes

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

Ich war bereits der Meinung, dass es möglich sein sollte, dies als Einzeiler zu tun, aber die Logik war zu komplex für mich. Nachdem ich die JavaScript-Antwort von @ user81655 und die Lambda-Notation in @ Tennis Python-answer gesehen habe, habe ich es erneut versucht.

Die Bedingung ist gleich

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

Leider spart der gesamte Transformationsaufwand nur ein No-Byte gegenüber der direkten Übersetzung (i<n/2or n%2)!=i%2der JavaScript-Logik.

übrigens
quelle
3
Willkommen bei Programming Puzzles & Code Golf! Dies scheint Python 2 zu sein, so dass Sie das int()um die Eingabe nicht benötigen . Sie können den Körper der for-Schleife auch in dieselbe Zeile setzen wie for....
Dennis
4

Python, 51 49 Bytes

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

Vielen Dank an @xnor für das Golfen mit 2 Bytes!

Probieren Sie es auf Ideone .

Wie es funktioniert

Wenn i eine Zahl in [0, ..., n - 1] ist , dann ist ~ i% n = - (i + 1)% n = - (i + 1) + n = (n - 1) - i , was bedeutet, dass es 0 auf n - 1 , 1 auf n - 2 und im Allgemeinen das j- te Element von links nach j- ten von rechts abbildet .

Wie in meiner Jelly-Antwort erläutert , können wir die Ausgabe konstruieren, indem wir den niedrigeren Wert zwischen i und ~ i% n betrachten und i auswählen, wenn er gerade ist, und ~ i% n, wenn er ungerade ist. Dies erreichen wir wie folgt.

  • Wenn die minimale gerade ist, min(i,~i%n)%-2wird nachgeben 0 , so XOR - Verknüpfung des Ergebnisses mit i ergeben wird i , und die Berechnung seiner Rest modulo n kehrt i .

  • Wenn die minimale ungerade ist, min(i,~i%n)%-2wird ergeben -1 , so dass das Ergebnis eine XOR - Verknüpfung mit i nachgeben ~ i , so dass die gesamte Ausdruck ergibt ~ i% n wie gewünscht.

Dennis
quelle
Sie können ein paar Zeichen speichern, indem Sie die Bedingung as ausführen (i^min(i,n+~i)%-2)%n.
Xnor
Das ist nicht nur kurz, sondern wahnsinnig clever. Vielen Dank!
Dennis
2

PHP, 77 76 51 50 49 Bytes

Verwendet ISO 8859-1-Codierung.

Setze die erste Hälfte des Arrays wie folgt zusammen:

  • Ungerade Zahlen haben ihren Indexwert (1, 3, 5 ..)
  • Gerade Zahlen haben den Wert N+1-index(9, 7, 5)
  • Das führt zu 1, 9, 3, 7, 5

Wie für die zweite Hälfte des Arrays addieren sich die äußersten Werte N+1, was bedeutet, dass Sie den entsprechenden rechten Wert erhalten, von N-[left value]dem der linke Wert bereits bekannt ist.

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

Gehen Sie wie folgt vor (dies zeigt auch die gesamte quadratische Differenz) ( -dnur aus ästhetischen Gründen hinzugefügt):

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
  • Durch Negieren der Links / Rechts-Bedingung wurde ein Byte gespeichert, sodass der zweite Ternär ohne Klammern verschachtelt werden kann
  • Einsparung von 25 Bytes durch schamlose Implementierung des Dennis-Algorithmus
  • Ein Byte wurde gespeichert, indem der benötigte Speicherplatz danach entfernt wurde echo
  • Ein Byte mit gespeichert , um ein Leerzeichen zu ergeben.
aross
quelle
1

Python 2, 100

Ich weiß, dass es bereits eine Python-Antwort gibt, aber ich glaube, dass ich das anders gemacht habe.

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

Und als Extra zum Testen der Gesamtpunktzahl:

def t(x,n):return sum((x[i]-x[(i+1)%n])**2for i in range(n))
Peter
quelle
def t(x,n):return sum((x[i]-x[i-1])**2for i in range(n))Verwendet das implizite Umbrechen von negativen Indizes und speichert 4 Bytes. Ich weiß, war nicht Teil des Wettbewerbs. ;)
Übrigens
1

CJam, 17 15 14 Bytes

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

Dies ist eine Funktion, die eine Ganzzahl n aus dem Stapel entfernt und eine Permutation von [0… n-1] zurückgibt. Der Code verwendet den gleichen Ansatz wie meine Gelee-Antwort .

Probieren Sie es online!

Wie es funktioniert

,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.
Dennis
quelle
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))))))))

Mit den Eingängen der Funktion können Sie den Anfangswert (m) und den Endwert (n) der Sequenz auswählen.

Zum Testen der Funktion gemäß den bereitgestellten Mustern wird n auf N und m auf 1 festgelegt.

Hier der Code zum Testen der Funktion:

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

Probieren Sie es auf Ideone !

PieCot
quelle
1

Julia, 39 Bytes

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

Dies gibt eine Permutation von 1: n aus . Eine Permutation von 0: n-1 kostet und spart keine Bytes:

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

Diese letzte Version ist ein direkter Port meiner Python-Antwort .

Dennis
quelle
0

ES6, 77 Bytes

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

Dabei werden i&1die Ziffern von den Extremen bis zur Mitte abgetastet. Das i&2fügt sie paarweise an den Anfang oder das Ende des Ergebnisses an.

Neil
quelle
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

bearbeiten ersetzt Buggy lange Version mit einer Implementierung von @ Dennis Jelly - Algorithmus

mnel
quelle