Comment trouver le chemin le plus court avec des nœuds de trou de ver?

25

Exemple

Ceci est un exemple de ce que je veux faire via du code. Je sais que vous pouvez utiliser la recherche de points de saut pour passer facilement du nœud vert au nœud rouge sans problème, ou même A *. Mais comment calculer cela avec des chaînes.

Dans l'image, vous pouvez voir qu'il ne faut que 8 mouvements pour passer du nœud vert au nœud rouge lors de la prise du chemin bleu. Le chemin bleu déplace instantanément votre position d'un nœud violet au suivant. L'espace au milieu qui coûte 2 mouvements est un point entre deux zones de distorsion que vous devez déplacer pour y accéder.

Il est clairement plus rapide de prendre le chemin bleu, car il suffit de se déplacer à peu près (à peu près) jusqu'au chemin jaune, mais comment faire par programme?

Pour résoudre ce problème, supposons qu'il existe plusieurs "déformations" violettes autour du graphique que vous pouvez utiliser, ET nous savons exactement où chaque point violet se déformera et où elles se trouvent sur le graphique.

Certaines chaînes violettes sont bidirectionnelles et d'autres non, ce qui signifie que parfois vous ne pouvez entrer dans une chaîne que d'un côté, mais pas revenir en arrière après la déformation.

J'ai réfléchi à la solution et j'ai seulement conclu que je serais en mesure de calculer le problème en vérifiant la distance à chaque point de distorsion (moins les points unidirectionnels), et la différence entre ces points et les points proches d'eux .

Le programme devrait comprendre d'une manière ou d'une autre qu'il est plus avantageux de prendre la deuxième chaîne, plutôt que de marcher depuis le premier saut. Donc, au lieu de déplacer 6 points, puis de déformer, puis de déplacer les 8 étapes restantes à pied (ce qui est également plus rapide que de ne pas utiliser de chaînes du tout), il faudrait les 6 mouvements, puis les deux mouvements vers la deuxième chaîne.

EDIT: J'ai réalisé que le chemin bleu prendra en fait 12 coups, au lieu de 8, mais la question reste la même.

Jeff smith
la source
4
Le chemin bleu ne doit-il pas être 12 (y compris les deux pour passer du dernier violet au rouge)?
BlueRaja - Danny Pflughoeft
5
Le bleu est en fait 12 (7 + 3 + 2) mouvements, non?
Daniel Jour
Oups, foiré, merci les gars! @DanielJour et Blue
Jeff smith
La façon "correcte" de modéliser les distances serait d'utiliser la topologie et de la modéliser comme une surface de dimension supérieure. Je me demande si une telle réponse serait appropriée ici?
Geeky I

Réponses:

49

La plupart des algorithmes de recherche de chemin sont définis en termes de graphiques, pas en termes de grilles. Dans un graphique, une connexion entre deux nœuds autrement distants n'est pas vraiment un problème.

Cependant, vous devez faire attention à votre heuristique. Avec les trous de ver, la distance minimale entre deux nœuds n'est plus la distance euclidienne et la distance ne satisfait pas l'inégalité du triangle. De telles heuristiques sont inadmissibles pour A *. Vous ne pouvez donc pas utiliser facilement A *.

Bien sûr, les algorithmes de recherche de chemin comme Dijkstra qui n'utilisent pas d'heuristique fonctionneront toujours. Cela ressemble plus à une recherche en largeur et sélectionnera vos trous de ver sans effort supplémentaire. Cependant, Dijkstra visitera plus de nœuds que A * avec une bonne heuristique. (Dijkstra est équivalent à A * avec heuristic(x) = 0.)

Je pense que A * fonctionnera si vous utilisez une heuristique qui traite tous les trous de ver sortants comme un trou de ver directement sur la cible: l'heuristique peut sous-estimer la distance, mais ne doit jamais la surestimer. C'est-à-dire que l'heuristique serait:

def wormhole_heuristic(x):
  return min(euclidean_distance(x, g) for g in [goal, wormholes...])

Pour une heuristique très précise, vous pouvez (récursivement) ajouter la distance entre le point d'extrémité du trou de ver et l'objectif ou le trou de ver suivant. C'est-à-dire qu'en tant que pré-calcul, vous pouvez effectuer la recherche de chemin sur le sous-graphique (totalement connecté) contenant tous les trous de ver et l'objectif, où la distance entre deux nœuds est leur distance euclidienne. Cela peut être bénéfique si le nombre de trous de ver est bien inférieur au nombre de cellules accessibles sur votre grille. La nouvelle heuristique serait:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  via_wormhole = min(euclidean_distance(x, w) + wormhole_path_distance(w, goal) for w in wormholes)
  return min(direct, via_wormhole)

Comme @Caleth le souligne dans les commentaires, tout cela est très ajustable et nous pouvons améliorer la première heuristique de trou de ver sans faire une recherche de chemin complète à travers le réseau de trou de ver, en ajoutant la distance entre la dernière sortie de trou de ver et l'objectif. Parce que nous ne savons pas quelle sortie de trou de ver sera utilisée en dernier et que nous ne devons pas surestimer, nous devons supposer la sortie la plus proche de l'objectif:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  to_next_wormhole = min(euclidean_distance(x, w) for w in wormholes)
  from_last_wormhole = min(euclidean_distance(w.exit, goal) for w in wormholes)
  via_wormhole = to_next_wormhole + from_last_wormhole
  return min(direct, via_wormhole)
amon
la source
10
À noter que Dijkstra n'est qu'un A * avecdijkstra_heuristic(x) = 0
Caleth
Je ne comprends pas ce que vous entendez par [* trous de ver, objectif], pourriez-vous expliquer cela?
Jeff smith
1
"La distance euclidienne à la sortie de trou de ver la plus proche" est une estimation moins coûteuse wormhole_path_distanceque la recherche sous-graphique, et moins sous-estimée que "toutes les sorties sont sur le but".
Caleth
3
@Caleth certainement! Il y a beaucoup de possibilités de réglage fin ici, par exemple, nous pourrions décider d'anticiper n = 3 sauts. La recherche sous-graphique correspond à la fermeture de tous les sauts de trous de ver acycliques. Votre suggestion de regarder vers l' avenir n = 1 sauts est très élégant car il a essentiellement zéro coût supplémentaire :)
amon
1
Pour plus de simplicité, supposons qu'il n'y ait qu'un seul trou de ver (deux nœuds), alors vous pouvez convertir ce plan 1 trou de ver en 2 plans de miroir, en copiant un plan symétrique avec la ligne équidistante entre ces deux points comme axe de symétrie. Vous avez maintenant deux plans, appelons-les le vrai avion (vous ne prenez pas le trou de ver) et le plan imaginaire (vous avez pris le trou de ver). Maintenant, nous introduisons la coordonnée Z. Cette coordonnée sera 0 pour chaque point du plan réel, et elle sera dist (trou de ver, point) pour chaque point du plan imaginaire. Après cela, appliquez A * pour l'espace tridimensionnel.
lilezek
5

Vous avez un graphe à 6 sommets sur une grille avec des coordonnées:

A ( 0,0)
B ( 4,7)
C ( 7,4)
D (10,4)
E (16,2)
F (16,0)

Vous pouvez générer un graphique complet sur ces sommets et attribuer un coût à chaque bord où le coût est MAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )pour les bords standard et un coût de 0 pour les trous de ver.

Cela vous donnera les coûts (en tant que matrice d'adjacence):

   A  B  C  D  E  F
- -- -- -- -- -- --
A  -  7  7 10 16 16
B  7  -  0  6 12 12
C  7  0  -  3  9  9
D 10  6  3  -  0  6
E 16 12  9  0  -  2
F 16 12  9  6  2  -

S'il y a des déformations unidirectionnelles, créez uniquement des arêtes dans le graphique (ou la matrice d'adjacence) qui vont dans cette direction mais pas dans le sens opposé.

Vous pouvez ensuite utiliser l'algorithme de Dijkstra avec une file d'attente prioritaire .

Commencez par Aet poussez chaque bord adjacent sur la file d'attente prioritaire:

Format: (chemin: coût)

queue     = [ (A-B : 7), (A-C : 7), (A-D : 10), (A-E : 16), (A-F : 16) ]

Au fur et à mesure que les éléments sont poussés dans la file d'attente - gardez une trace du coût minimum pour chaque sommet et ne poussez les chemins dans la file d'attente que si leur coût est inférieur au coût minimum existant.

min-costs = { A: 0, B: 7, C: 7, D: 10, E: 16, F: 16 }

Supprimez le premier élément de la file d'attente et, si son coût correspond toujours au coût minimum, repoussez tous les chemins composites formés par ce chemin et ses bords adjacents dans la file d'attente prioritaire (si les chemins composites ont un coût inférieur au minimum existant):

Retirer: (A-B : 7)

  • Essayer (A-B-A : 14)- rejeter comme coût plus élevé
  • Essayer (A-B-C : 7)- rejeter au même coût
  • Essayer (A-B-D : 13)- rejeter comme coût plus élevé
  • Essayer (A-B-E : 19)- rejeter comme coût plus élevé
  • Essayer (A-B-F : 19)- rejeter comme coût plus élevé

Retirer (A-C : 7)

  • Essayer (A-C-A : 14)- rejeter comme coût plus élevé
  • Essayer (A-C-B : 7)- rejeter au même coût
  • Essayer (A-C-D : 10)- rejeter au même coût
  • Essayer (A-C-E : 16)- rejeter au même coût
  • Essayer (A-C-F : 16)- rejeter au même coût

Retirer (A-D : 10)

  • Essayer (A-D-A : 20)- rejeter comme coût plus élevé
  • Essayer (A-D-B : 16)- rejeter comme coût plus élevé
  • Essayer (A-D-C : 13)- rejeter comme coût plus élevé
  • Essayer (A-D-E : 10)- insérer dans la file d'attente
  • Essayer (A-D-F : 16)- rejeter au même coût

Maintenant, la file d'attente ressemblera à:

queue     = [ (A-D-E : 10), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 16 }

Retirer (A-D-E : 10)

  • Essayer (A-D-E-A : 26)- rejeter comme coût plus élevé
  • Essayer (A-D-E-B : 22)- rejeter comme coût plus élevé
  • Essayer (A-D-E-C : 19)- rejeter comme coût plus élevé
  • Essayer (A-D-E-D : 10)- rejeter au même coût
  • Essayer (A-D-E-F : 12)- insérer dans la file d'attente

Ensuite, la file d'attente est:

queue     = [ (A-D-E-F : 12), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 12 }

Supprimez (A-D-E-F : 12), trouvez que vous êtes arrivé au nœud de destination au coût de 12.

Remarque: les chemins (A-B-C-D-E-F), (A-C-D-E-F)et (A-D-E-F)tous ont le même coût minimum de 12.

MT0
la source
0

Configurez une matrice contenant tous les sommets et utilisez l'algorithme Floyd-Wallenstein ou l'algorithme Bellman-Ford. Les deux résulteront en une matrice avec les chemins les plus courts possibles entre tous les points. Vous pouvez ensuite parcourir la matrice pour trouver le chemin le plus court reliant deux points. (votre problème est le même qu'un TSP asymétrique).

René
la source