L o o p I t


22

Nota: il titolo di questa domanda dovrebbe essere "Loop It", ma poiché il titolo deve contenere almeno 15 caratteri, ci sono alcuni spazi invisibili. Questa nota è tale che la sfida può essere cercata.


Sfida

Dato un elenco finito di punti integrali univoci nel piano, trova un poligono i cui vertici sono esattamente quei punti, che non si intersecano da soli.

Dettagli

  • Come input puoi prendere ad esempio due elenchi con ciascuna le coordinate x e y o un elenco di coppie.
  • L'elenco di input contiene almeno 3 punti.
  • Si noti che ciò significa che non esiste mai una soluzione unica.
  • Si può presumere che l'elenco degli input non sia co-lineare (i punti non possono essere contenuti in una riga), ciò significa che esiste effettivamente un poligono non autointersecante.
  • Gli angoli di ciascun vertice sono arbitrari, questo include 180 °.
  • Per un input di lunghezza n, l'output dovrebbe essere una permutazione (p1,p2,p3,...,pn)del punto in (1,2,3,...,n)cui la kvoce -th pkrappresenta il ppunto -th nell'elenco di input. Ciò significa che abbiamo una linea da p1a p2, una linea da p2a p3ecc., Nonché una linea da pna p1. (È inoltre possibile utilizzare gli indici basati su 0). In alternativa, è possibile generare semplicemente l'elenco dei punti di input nell'ordine corretto.

Esempi

Diciamo che abbiamo i punti [(0,0),(0,1),(1,0),(-1,0),(0,-1)]e vogliamo rappresentare il seguente percorso:

inserisci qui la descrizione dell'immagine

Ciò significa che avremmo prodotto l'elenco [5,1,4,2,3]

Ecco qualche altro suggerimento da provare (consiglio di guardare i grafici corrispondenti per verificare gli obiettivi).

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

Se abbiamo 4 punti O (0,0), A (1,0), B (0,1), C (0,2), il poligono OABC è autointersecante?
ngn,

@ngn Questo è un buon punto che non ho considerato! Ci dovrò pensare. Se avete qualche argomento a favore o contro questo, per favore fatemelo sapere.
flawr

@ngn Direi che questo poligono si interseca da solo. Il motivo è che definirei un poligono che si interseca da solo se esiste un punto comune di due spigoli che non è un endpoint.
flawr

@flawr Devo ritirare la mia risposta quindi, non riesce quando ci sono più punti co-lineari con l'angolo massimo dal punto di riferimento.
ngn

Risposte:


10

Mathematica 29 28 byte

FindShortestTour (16 byte) fa il trucco ma fornisce alcune informazioni estranee non richieste (la lunghezza del percorso e un ritorno al punto di partenza).

Most@*Last@*FindShortestTour

dà solo la risposta (-1 byte grazie a @ user202729)

Per visualizzare, utilizzare Graphics@Line[g[[%]]], dove %è la permutazione trovata sopra e g è l'elenco di punti originale.

Ecco la visualizzazione della soluzione per la Spugna Menger: inserisci qui la descrizione dell'immagine

Ecco una soluzione su 1000 punti casuali:

inserisci qui la descrizione dell'immagine

La chiave qui è sapere che la soluzione del problema del venditore o del viaggiatore più breve non produrrà mai incroci quando la distanza euclidea viene utilizzata come metrica. Uno dei passaggi per localizzare una soluzione e garantire l'ottimalità è rimuovere tali incroci.


6
Utilizzare l'algoritmo NP per risolvere il problema P solo perché è più breve. +1 (???).
user202729

1
@*sembra salvare un byte.
user202729


6

JavaScript (ES6), 365 341 byte

Senza alcun built-in, questo si è rivelato molto più lungo di quanto mi aspettassi. Molti byte vengono spesi per rilevare segmenti sovrapposti collineari.

Accetta input come una matrice di [x,y]coordinate. Restituisce una permutazione dell'ingresso.

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

dimostrazione

Questo frammento registra l'output e disegna il percorso corrispondente in un'area di disegno.

Come?

Ecco la struttura della principale funzione ricorsiva f () , lasciando da parte il codice di test di intersezione per ora:

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

Di seguito è riportato il dettaglio del test di intersezione () . Questa pagina fornisce una spiegazione esauriente dell'algoritmo utilizzato.

[                                     // 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

Infine, ecco la definizione della funzione helper 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

... spiegazione per favore?
user202729

1
@ user202729 (* si passa la mano sulla fronte *) Fatto!
Arnauld,

5

APL (Dyalog Classic) , 42 38 byte

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

Provalo online!

L'input è un elenco di coppie di coordinate. L'output è una permutazione basata su 0.

è l'elenco di punti - l'argomento a { }

⍵[⊃⍋↑⍵] è il punto più basso a sinistra

⍵- traduce tutti i punti in modo tale che l'estrema sinistra sia all'origine del sistema di coordinate

0j1 l'unità immaginaria i = sqrt (-1)

0j1⊥¨ decodifica le coordinate come se fossero cifre in un sistema numerico base-i - ovvero trasforma (x, y) in un numero complesso ix + y

z← assegnato a z

12○calcola gli argomenti dei numeri complessi, ovvero gli angoli theta o la funzione circolare APL 12

(⍪,(|z)ׯ1*⊢=⌈/)è un treno che calcola una maschera booleana in cui gli angoli sono al massimo ( ⊢=⌈/), trasforma lo 0 1 nella maschera in 1 ¯1 aumentando ¯1 alla potenza corrispondente ( ¯1*), moltiplica per l'entità dei numeri complessi |z, e lo concatena a destra ( ,) di una matrice a 1 colonna alta e sottile ( ) degli angoli.

grade - restituisce la permutazione che ordinerebbe lessicograficamente le file della matrice in ordine crescente


@utente202729 verranno ordinati in base al secondo criterio: distanza (ovvero funzione circolare 10,
nota anche come
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.