L o o p I t

22

Remarque: Le titre de cette question doit être "Loop It", mais comme le titre doit comporter au moins 15 caractères, il existe des espaces invisibles. Cette note est telle que le défi peut être recherché.


Défi

Étant donné une liste finie de points intégraux uniques dans le plan, trouvez un polygone dont les sommets sont exactement ces points, qui ne se recoupent pas automatiquement.

Détails

  • En entrée, vous pouvez prendre par exemple deux listes avec chacune les coordonnées x et y ou une liste de paires.
  • La liste d'entrée contient au moins 3 points.
  • Notez que cela signifie qu'il n'y a jamais de solution unique.
  • On peut supposer que la liste des entrées n'est pas colinéaire (les points ne peuvent pas être contenus sur une seule ligne), cela signifie qu'il existe en fait un tel polygone non auto-intersecté.
  • Les angles à chaque sommet sont arbitraires, cela inclut 180 °.
  • Pour une entrée de longueur n, la sortie doit être une permutation (p1,p2,p3,...,pn)de l' (1,2,3,...,n)endroit où la k-ème entrée pkreprésente le p-ème point dans la liste des entrées. Cela signifie que nous avons une ligne de p1à p2, une ligne de p2à p3etc, ainsi qu'une ligne de pnà p1. (Vous pouvez également utiliser les indices basés sur 0.) Vous pouvez également simplement afficher la liste des points d'entrée dans le bon ordre.

Exemples

Disons que nous avons les points [(0,0),(0,1),(1,0),(-1,0),(0,-1)]et que nous voulons représenter le chemin suivant:

entrez la description de l'image ici

Cela signifie que nous publierions la liste [5,1,4,2,3]

Voici quelques suggestions supplémentaires à essayer (je recommande de regarder les parcelles correspondantes pour vérifier les objectifs.)

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)]
flawr
la source
Si nous avons 4 points O (0,0), A (1,0), B (0,1), C (0,2), le polygone OABC est-il auto-intersecté?
ngn
@ngn C'est un bon point que je n'ai pas considéré! Je vais devoir y penser. Si vous avez des arguments pour ou contre cela, faites-le moi savoir.
flawr
@ngn Je compterais ce polygone comme auto-intersecté. La raison en est que je définirais un polygone à auto-intersection s'il y a un point commun de deux arêtes qui n'est pas une extrémité.
flawr
@flawr Je dois alors retirer ma réponse, elle échoue quand il y a plusieurs points colinéaires à l'angle maximum du point de référence.
ngn

Réponses:

10

Mathematica 29 28 octets

FindShortestTour (16 octets) fait l'affaire mais fournit des informations superflues non demandées (la longueur du chemin et un retour au point de départ).

Most@*Last@*FindShortestTour

donne juste la réponse (-1 octet grâce à @ user202729)

Pour visualiser, utilisez Graphics@Line[g[[%]]], où %est la permutation trouvée ci-dessus et g est la liste de points d'origine.

Voici la visualisation de la solution pour l'éponge Menger: entrez la description de l'image ici

Voici une solution sur 1000 points aléatoires:

entrez la description de l'image ici

La clé ici est de savoir que la solution de problème de tour ou de vendeur le plus court ne produira jamais d'intersections lorsque la distance euclidienne est utilisée comme métrique. L'une des étapes pour localiser une solution et garantir l'optimalité est de supprimer ces intersections.

Kelly Lowder
la source
6
Utilisez l'algorithme NP pour résoudre le problème P simplement parce qu'il est plus court. +1 (???).
user202729
1
@*semble enregistrer un octet.
user202729
6

JavaScript (ES6), 365 341 octets

Sans aucune fonction intégrée, cela s'est avéré être beaucoup plus long que prévu. De nombreux octets sont consacrés à la détection de segments colinéaires se chevauchant.

Prend l'entrée comme un tableau de [x,y]coordonnées. Renvoie une permutation de l'entrée.

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

Démo

Cet extrait enregistre la sortie et trace le chemin d'accès correspondant dans un canevas.

Comment?

Voici la structure de la fonction récursive principale f () , en laissant de côté le code de test d'intersection pour l'instant:

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

Voici le détail du test intersection () . Cette page fournit une explication complète sur l'algorithme utilisé.

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

Enfin, voici la définition de la fonction d'aide 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
la source
... explication s'il vous plait?
user202729
1
@ user202729 (* essuie la main sur le front *) Terminé!
Arnauld
5

APL (Dyalog Classic) , 42 38 octets

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

Essayez-le en ligne!

L'entrée est une liste de paires de coordonnées. La sortie est une permutation basée sur 0.

est la liste des points - l'argument de { }

⍵[⊃⍋↑⍵] est le point le plus à gauche le plus bas

⍵- traduit tous les points de façon à ce que l'extrême gauche soit à l'origine du système de coordonnées

0j1 l'unité imaginaire i = sqrt (-1)

0j1⊥¨ décode les coordonnées comme si des chiffres dans un système numérique en base i - c'est-à-dire transforme (x, y) en un nombre complexe ix + y

z← affecter à z

12○calcule les arguments des nombres complexes, alias les angles thêta, ou la fonction circulaire APL 12

(⍪,(|z)ׯ1*⊢=⌈/)est un train qui calcule un masque booléen où les angles sont au maximum ( ⊢=⌈/), transforme le 0 1 dans le masque en 1 ¯1 en élevant ¯1 à la puissance correspondante ( ¯1*), multiplié par les magnitudes des nombres complexes |z, et concatène celle à droite ( ,) d'une matrice haute et mince à 1 colonne ( ) des angles.

grade - renvoie la permutation qui trierait les lignes de la matrice lexicographiquement dans l'ordre croissant

ngn
la source
@ user202729 ils seront triés selon le deuxième critère - distance (c'est-à-dire fonction circulaire 10, alias magnitude complexe)
ngn