L o o p I t

22

Hinweis: Der Titel dieser Frage sollte "Loop It" lauten. Da der Titel jedoch mindestens 15 Zeichen enthalten muss, sind einige Leerzeichen nicht sichtbar. Diese Notiz ist so, dass nach der Herausforderung gesucht werden kann.


Herausforderung

Suchen Sie anhand einer endlichen Liste eindeutiger integraler Punkte in der Ebene ein Polygon, dessen Eckpunkte genau die Punkte sind, die sich nicht selbst schneiden.

Einzelheiten

  • Als Eingabe können Sie zB zwei Listen mit den x- und y-Koordinaten oder eine Liste von Paaren verwenden.
  • Die Eingabeliste enthält mindestens 3 Punkte.
  • Beachten Sie, dass dies bedeutet, dass es niemals eine eindeutige Lösung gibt.
  • Es kann davon ausgegangen werden, dass die Liste der Eingaben nicht kolinear ist (die Punkte können nicht in einer Linie enthalten sein). Dies bedeutet, dass es tatsächlich ein solches sich nicht selbst schneidendes Polygon gibt.
  • Die Winkel an jedem Scheitelpunkt sind beliebig und umfassen 180 °.
  • Für eine Eingabe von Länge n, sollte die Ausgabe eine Permutation (p1,p2,p3,...,pn)von (1,2,3,...,n)wo der k-te Eintrag pkdie repräsentiert p-ten Punkt in der Eingangsliste. Dies bedeutet, dass wir eine Linie von p1bis p2, eine Linie von p2bis p3usw. sowie eine Linie von pnbis haben p1. (Sie können auch die auf 0 basierenden Indizes verwenden.) Alternativ können Sie die Liste der Eingabepunkte auch in der richtigen Reihenfolge ausgeben.

Beispiele

Angenommen, wir haben die Punkte [(0,0),(0,1),(1,0),(-1,0),(0,-1)]und möchten den folgenden Pfad darstellen:

Bildbeschreibung hier eingeben

Das heißt, wir würden die Liste ausgeben [5,1,4,2,3]

Hier noch ein paar Vorschläge zum Ausprobieren (Ich empfehle, die entsprechenden Grundstücke zu betrachten, um die Ziele zu überprüfen.)

Triangle
[(0,0),(0,1),(1,0)]

S-Curve
[(0,0),(0,1),(0,2),(0,3),(0,4),(1,0),(2,0),(2,1),(2,2),(2,3),(2,4),(3,4),(4,0),(4,1),(4,2),(4,3),(4,4)]

L-Shape
[(4,0),(1,0),(3,0),(0,0),(2,0),(0,1)]

Menger Sponge
[(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),(18,1),(19,1),(20,1),(21,1),(22,1),(23,1),(24,1),(25,1),(26,1),(27,1),(1,2),(3,2),(4,2),(6,2),(7,2),(9,2),(10,2),(12,2),(13,2),(15,2),(16,2),(18,2),(19,2),(21,2),(22,2),(24,2),(25,2),(27,2),(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),(8,3),(9,3),(10,3),(11,3),(12,3),(13,3),(14,3),(15,3),(16,3),(17,3),(18,3),(19,3),(20,3),(21,3),(22,3),(23,3),(24,3),(25,3),(26,3),(27,3),(1,4),(2,4),(3,4),(7,4),(8,4),(9,4),(10,4),(11,4),(12,4),(16,4),(17,4),(18,4),(19,4),(20,4),(21,4),(25,4),(26,4),(27,4),(1,5),(3,5),(7,5),(9,5),(10,5),(12,5),(16,5),(18,5),(19,5),(21,5),(25,5),(27,5),(1,6),(2,6),(3,6),(7,6),(8,6),(9,6),(10,6),(11,6),(12,6),(16,6),(17,6),(18,6),(19,6),(20,6),(21,6),(25,6),(26,6),(27,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7),(7,7),(8,7),(9,7),(10,7),(11,7),(12,7),(13,7),(14,7),(15,7),(16,7),(17,7),(18,7),(19,7),(20,7),(21,7),(22,7),(23,7),(24,7),(25,7),(26,7),(27,7),(1,8),(3,8),(4,8),(6,8),(7,8),(9,8),(10,8),(12,8),(13,8),(15,8),(16,8),(18,8),(19,8),(21,8),(22,8),(24,8),(25,8),(27,8),(1,9),(2,9),(3,9),(4,9),(5,9),(6,9),(7,9),(8,9),(9,9),(10,9),(11,9),(12,9),(13,9),(14,9),(15,9),(16,9),(17,9),(18,9),(19,9),(20,9),(21,9),(22,9),(23,9),(24,9),(25,9),(26,9),(27,9),(1,10),(2,10),(3,10),(4,10),(5,10),(6,10),(7,10),(8,10),(9,10),(19,10),(20,10),(21,10),(22,10),(23,10),(24,10),(25,10),(26,10),(27,10),(1,11),(3,11),(4,11),(6,11),(7,11),(9,11),(19,11),(21,11),(22,11),(24,11),(25,11),(27,11),(1,12),(2,12),(3,12),(4,12),(5,12),(6,12),(7,12),(8,12),(9,12),(19,12),(20,12),(21,12),(22,12),(23,12),(24,12),(25,12),(26,12),(27,12),(1,13),(2,13),(3,13),(7,13),(8,13),(9,13),(19,13),(20,13),(21,13),(25,13),(26,13),(27,13),(1,14),(3,14),(7,14),(9,14),(19,14),(21,14),(25,14),(27,14),(1,15),(2,15),(3,15),(7,15),(8,15),(9,15),(19,15),(20,15),(21,15),(25,15),(26,15),(27,15),(1,16),(2,16),(3,16),(4,16),(5,16),(6,16),(7,16),(8,16),(9,16),(19,16),(20,16),(21,16),(22,16),(23,16),(24,16),(25,16),(26,16),(27,16),(1,17),(3,17),(4,17),(6,17),(7,17),(9,17),(19,17),(21,17),(22,17),(24,17),(25,17),(27,17),(1,18),(2,18),(3,18),(4,18),(5,18),(6,18),(7,18),(8,18),(9,18),(19,18),(20,18),(21,18),(22,18),(23,18),(24,18),(25,18),(26,18),(27,18),(1,19),(2,19),(3,19),(4,19),(5,19),(6,19),(7,19),(8,19),(9,19),(10,19),(11,19),(12,19),(13,19),(14,19),(15,19),(16,19),(17,19),(18,19),(19,19),(20,19),(21,19),(22,19),(23,19),(24,19),(25,19),(26,19),(27,19),(1,20),(3,20),(4,20),(6,20),(7,20),(9,20),(10,20),(12,20),(13,20),(15,20),(16,20),(18,20),(19,20),(21,20),(22,20),(24,20),(25,20),(27,20),(1,21),(2,21),(3,21),(4,21),(5,21),(6,21),(7,21),(8,21),(9,21),(10,21),(11,21),(12,21),(13,21),(14,21),(15,21),(16,21),(17,21),(18,21),(19,21),(20,21),(21,21),(22,21),(23,21),(24,21),(25,21),(26,21),(27,21),(1,22),(2,22),(3,22),(7,22),(8,22),(9,22),(10,22),(11,22),(12,22),(16,22),(17,22),(18,22),(19,22),(20,22),(21,22),(25,22),(26,22),(27,22),(1,23),(3,23),(7,23),(9,23),(10,23),(12,23),(16,23),(18,23),(19,23),(21,23),(25,23),(27,23),(1,24),(2,24),(3,24),(7,24),(8,24),(9,24),(10,24),(11,24),(12,24),(16,24),(17,24),(18,24),(19,24),(20,24),(21,24),(25,24),(26,24),(27,24),(1,25),(2,25),(3,25),(4,25),(5,25),(6,25),(7,25),(8,25),(9,25),(10,25),(11,25),(12,25),(13,25),(14,25),(15,25),(16,25),(17,25),(18,25),(19,25),(20,25),(21,25),(22,25),(23,25),(24,25),(25,25),(26,25),(27,25),(1,26),(3,26),(4,26),(6,26),(7,26),(9,26),(10,26),(12,26),(13,26),(15,26),(16,26),(18,26),(19,26),(21,26),(22,26),(24,26),(25,26),(27,26),(1,27),(2,27),(3,27),(4,27),(5,27),(6,27),(7,27),(8,27),(9,27),(10,27),(11,27),(12,27),(13,27),(14,27),(15,27),(16,27),(17,27),(18,27),(19,27),(20,27),(21,27),(22,27),(23,27),(24,27),(25,27),(26,27),(27,27)]
fehlerhaft
quelle
Wenn wir 4 Punkte O (0,0), A (1,0), B (0,1), C (0,2) haben, schneidet sich das Polygon OABC selbst?
13.
@ngn Das ist ein guter Punkt, den ich nicht bedacht habe! Ich muss darüber nachdenken. Wenn Sie ein Argument dafür oder dagegen haben, lassen Sie es mich bitte wissen.
Fehler
@ngn Ich würde dieses Polygon als sich selbst schneidend zählen. Der Grund ist, dass ich ein Polygon so definieren würde, dass es sich selbst schneidet, wenn es einen gemeinsamen Punkt von zwei Kanten gibt, der kein Endpunkt ist.
Fehler
@flawr Ich muss meine Antwort dann zurückziehen, es schlägt fehl, wenn es mehrere kolineare Punkte im maximalen Winkel vom Referenzpunkt gibt.
13.

Antworten:

10

Mathematica 29 28 Bytes

FindShortestTour (16 Bytes) erledigt den Trick, liefert aber einige nicht angeforderte Informationen (die Pfadlänge und eine Rückkehr zum Startpunkt).

Most@*Last@*FindShortestTour

gibt nur die Antwort (-1 Byte dank @ user202729)

Verwenden Sie zur Visualisierung, Graphics@Line[g[[%]]]wobei %die oben angegebene Permutation und g die ursprüngliche Punktliste ist.

Hier ist die Visualisierung der Lösung für den Menger-Schwamm: Bildbeschreibung hier eingeben

Hier ist eine Lösung für 1000 zufällige Punkte:

Bildbeschreibung hier eingeben

Der Schlüssel dabei ist zu wissen, dass die Lösung des Problems der kürzesten Tour oder des Problems des Handelsreisenden niemals zu Kreuzungen führt, wenn die euklidische Entfernung als Metrik verwendet wird. Einer der Schritte zur Lokalisierung einer Lösung und zur Sicherstellung der Optimalität besteht darin, solche Überschneidungen zu entfernen.

Kelly Lowder
quelle
6
Verwenden Sie den NP-Algorithmus, um das P-Problem zu lösen, nur weil es kürzer ist. +1 (???).
user202729
1
@*scheint ein Byte zu retten.
user202729
6

JavaScript (ES6), 365 341 Byte

Es stellte sich heraus, dass dies viel länger war, als ich erwartet hatte. Viele Bytes werden zum Erkennen kollinearer überlappender Segmente benötigt.

Nimmt die Eingabe als ein Array von [x,y]Koordinaten. Gibt eine Permutation der Eingabe zurück.

f=(a,p=[],o=([p,P],[q,Q],[r,R])=>Math.sign((S=[(p>q?r<q|r>p:r<p|r>q)|(P>Q?R<Q|R>P:R<P|R>Q),...S],Q-P)*(r-q)-(q-p)*(R-Q)))=>[...p,p[0]].some((A,i,P)=>P.some((C,j)=>j>i+1&&P[++j+!i]&&[E=o(A,B=P[i+1],C,S=[]),F=o(A,B,D=P[j]),G=o(C,D,A),H=o(C,D,B)].some(v=>!v&!S.pop())|E!=F&G!=H))?0:a[0]?a.some((_,i)=>r=f(b=[...a],p.concat(b.splice(i,1))))&&r:p

Demo

Dieses Snippet protokolliert die Ausgabe und zeichnet den entsprechenden Pfad in eine Zeichenfläche.

Wie?

Hier ist die Struktur der rekursiven Hauptfunktion f () , wobei der Schnittpunkttestcode zunächst weggelassen wird:

f = (a, p = []) =>                    // a = array of points, p = current path
  [...p,                              // build a closed path array P[] by adding the first
         p[0]]                        // point at the end of p[]
  .some((A, i, P) =>                  // for each point A at position i in P:
    P.some((C, j) =>                  //   for each point C at position j in P:
      j > i + 1 &&                    //     test whether C is at least 2 positions after A
      P[++j +                         //     and C is not the last point
              !i] &&                  //     and i > 0 or C is not the penultimate point
      intersection(                   //     and there's an intersection between
        A, P[i + 1], C, P[j]          //     the segments (A, P[i + 1]) and (C, P[j + 1])
      )                               //     (j was incremented above)
    )                                 //   end of inner some()
  ) ?                                 // end of outer some(); if truthy:
    0                                 //   discard this path by stopping recursion
  :                                   // else:
    a[0] ?                            //   if there's at least one remaining point:
      a.some((_, i) =>                //     for each remaining point at position i:
        r = f(                        //       do a recursive call with:
          b = [...a],                 //         a copy b[] of a[] without a[i] and
          p.concat(b.splice(i, 1)))   //         the extracted point added to the path
      ) && r                          //     end of some(); return the result, if any
    :                                 //   else:
      p                               //     this is a valid path: return it

Unten ist das Detail des intersection () -Tests. Auf dieser Seite finden Sie eine umfassende Erläuterung des verwendeten Algorithmus.

[                                     // build an array containing:
  E = o(A, B = P[i + 1], C, S = []),  //   E = test of (A, B, C) (+ initialization of S[])
  F = o(A, B, D = P[j]),              //   F = test of (A, B, D)
  G = o(C, D, A),                     //   G = test of (C, D, A)
  H = o(C, D, B)                      //   H = test of (C, D, B)
]                                     //
.some(v =>                            // the segments are collinear and overlapping if:
  !v &                                //   any value above is 0
  !S.pop()                            //   and the corresponding entry in S[] is falsy
) |                                   // the segments intersect if:
E != F & G != H                       //   E is not equal to F and G is not equal to H

Schließlich ist hier die Definition der Hilfsfunktion o () :

o = (                                             // given three points represented by
  [p, P], [q, Q], [r, R]                          // a lowercase letter for x
) =>                                              // and an uppercase letter for y:
  Math.sign(                                      //
    (                                             //   1) prepend to the array S[]
      S = [                                       //      a boolean which is true if the
        (p > q ? r < q | r > p : r < p | r > q) | //      segment (P, Q) would not contain
        (P > Q ? R < Q | R > P : R < P | R > Q),  //      the point R, assuming that the
        ...S                                      //      3 points are collinear
      ],                                          //
                                                  //   2) return the orientation of P, Q, R:
      Q - P                                       //        -1 = counterclockwise
    ) * (r - q) - (q - p) * (R - Q)               //         0 = collinear
  )                                               //        +1 = clockwise
Arnauld
quelle
... bitte erklärung?
user202729
1
@ user202729 (* wischt sich die Hand über die Stirn *) Fertig!
Arnauld
5

APL (Dyalog Classic) , 42 38 Bytes

{⍋(⍪,(|z)ׯ1*⊢=⌈/)12z0j1⊥¨⍵-⍵[⊃⍋↑⍵]}

Probieren Sie es online!

Die Eingabe ist eine Liste von Koordinatenpaaren. Die Ausgabe ist eine 0-basierte Permutation.

ist die Liste der Punkte - das Argument für { }

⍵[⊃⍋↑⍵] ist der am weitesten links liegende Punkt

⍵- Verschiebt alle Punkte so, dass sich die am weitesten links liegende am Ursprung des Koordinatensystems befindet

0j1 die imaginäre Einheit i = sqrt (-1)

0j1⊥¨ Dekodiert die Koordinaten wie Ziffern in einem Basis-I-Zahlensystem - dh verwandelt (x, y) in eine komplexe Zahl ix + y

z← zuweisen z

12○berechnet die Argumente der komplexen Zahlen, auch Theta-Winkel genannt, oder die APL-Kreisfunktion 12

(⍪,(|z)ׯ1*⊢=⌈/)ist ein Zug, der eine boolesche Maske berechnet, bei der die Winkel maximal sind ( ⊢=⌈/), die 0 1 in der Maske durch Erhöhen von ¯1 auf die entsprechende Potenz ( ¯1*) in 1 ¯1 umwandelt , multipliziert mit den Beträgen der komplexen Zahlen |z, und verkettet das rechts ( ,) einer hohen dünnen 1-Spalten-Matrix ( ) der Winkel.

grade - Gibt die Permutation zurück, mit der die Zeilen der Matrix lexikographisch in aufsteigender Reihenfolge sortiert werden

ngn
quelle
Abstand (dh Kreisfunktion 10, auch bekannt als komplexe Größe) - @ user202729 werden sie gemäß dem zweiten Kriterium sortiert werden
NGN