web-dev-qa-db-fra.com

Trouvez le chemin le plus court dans un graphique qui visite certains nœuds

J'ai un graphique non orienté avec environ 100 nœuds et environ 200 bords. Un nœud est étiqueté "début", l'autre est "fin", et il y a environ une douzaine d'étiquettes "mustpass".

J'ai besoin de trouver le chemin le plus court à travers ce graphique qui commence à 'start', se termine à 'end', et passe par tous les nœuds 'mustpass' (dans n'importe quel ordre).

( http://3e.org/local/maize-graph.png / http://3e.org/local/maize-graph.dot.txt est le graphique en question - il représente un labyrinthe de maïs à Lancaster, PA)

72
dmd

Tout le monde comparant cela au problème du voyageur de commerce n'a probablement pas lu attentivement votre question. Dans TSP, l'objectif est de trouver le cycle le plus court qui visite tous les sommets (un cycle hamiltonien) - cela correspond à avoir tous les nœuds étiquetés "mustpass".

Dans votre cas, étant donné que vous n'en avez qu'une douzaine étiquetée "mustpass", et étant donné que 12! est assez petit (479001600), vous pouvez simplement essayer toutes les permutations des nœuds "mustpass" uniquement, et regarder le chemin le plus court de "début" à "fin" qui visite les nœuds "mustpass" dans cet ordre - il être la concaténation des chemins les plus courts entre deux nœuds consécutifs de cette liste.

En d'autres termes, trouvez d'abord la distance la plus courte entre chaque paire de sommets (vous pouvez utiliser l'algorithme de Dijkstra ou d'autres, mais avec ces petits nombres (100 nœuds), même le plus simple à coder algorithme Floyd-Warshall = fonctionnera dans le temps). Ensuite, une fois que vous avez ceci dans une table, essayez toutes les permutations de vos nœuds "mustpass", et le reste.

Quelque chose comme ça:

//Precomputation: Find all pairs shortest paths, e.g. using Floyd-Warshall
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
    for i=1 to n:
        for j=1 to n:
            d[i][j] = min(d[i][j], d[i][k] + d[k][j])
//That *really* gives the shortest distance between every pair of nodes! :-)

//Now try all permutations
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
    shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest

(Bien sûr, ce n'est pas du vrai code, et si vous voulez le chemin réel, vous devrez garder une trace de la permutation qui donne la distance la plus courte, et aussi des chemins les plus courts toutes paires, mais vous avez l'idée.)

Il s'exécutera en quelques secondes au maximum sur n'importe quelle langue raisonnable :)
[Si vous avez n nœuds et k nœuds "mustpass", son temps d'exécution est O (n3) pour la partie Floyd-Warshall, et O (k! n) pour la partie toutes permutations, et 100 ^ 3 + (12!) (100) est pratiquement des arachides, sauf si vous avez des contraintes vraiment restrictives.]

72
ShreevatsaR

run Djikstra's Algorithm pour trouver les chemins les plus courts entre tous les nœuds critiques (début, fin et passage obligé), puis une traversée en profondeur en premier devrait vous indiquer le chemin le plus court à travers le sous-graphique résultant qui touche tous les nœuds commencent ... doivent passer ... fin

23
Steven A. Lowe

En fait, le problème que vous avez signalé est similaire au vendeur ambulant, mais je pense plus proche d'un simple problème d'orientation. Plutôt que de devoir visiter chaque nœud, il vous suffit de visiter un ensemble particulier de nœuds dans le temps (distance) le plus court possible.

La raison en est que, contrairement au problème du voyageur de commerce, un labyrinthe de maïs ne vous permettra pas de voyager directement d'un point à un autre point de la carte sans avoir à passer par d'autres nœuds pour y arriver.

Je recommanderais en fait A * pathfinding comme technique à considérer. Vous configurez cela en décidant quels nœuds ont accès à quels autres nœuds directement, et quel est le "coût" de chaque saut à partir d'un nœud particulier. Dans ce cas, il semble que chaque "saut" puisse être de même coût, car vos nœuds semblent relativement rapprochés. A * peut utiliser ces informations pour trouver le chemin le plus économique entre deux points. Puisque vous devez vous rendre du point A au point B et visiter environ 12 entre les deux, même une approche par force brute utilisant la recherche de chemin ne ferait pas de mal du tout.

Juste une alternative à considérer. Il ressemble remarquablement au problème des vendeurs itinérants, et ce sont de bons documents à lire, mais regardez de plus près et vous verrez que ce ne sont que des choses trop compliquées. ^ _ ^ Cela vient de l'esprit d'un programmeur de jeux vidéo qui a déjà traité ce genre de choses.

14
Nicholas Flynt

Ce sont deux problèmes ... Steven Lowe l'a souligné, mais n'a pas accordé suffisamment de respect à la seconde moitié du problème.

Vous devez d'abord découvrir les chemins les plus courts entre tous vos nœuds critiques (début, fin, mustpass). Une fois ces chemins découverts, vous pouvez construire un graphique simplifié, où chaque Edge dans le nouveau graphique est un chemin d'un nœud critique à un autre dans le graphique d'origine. Il existe de nombreux algorithmes de recherche de chemin que vous pouvez utiliser pour trouver le chemin le plus court ici.

Une fois que vous avez ce nouveau graphique, cependant, vous avez exactement le problème du voyageur de commerce (enfin, presque ... Pas besoin de revenir à votre point de départ). Tous les messages à ce sujet, mentionnés ci-dessus, s'appliqueront.

14
Andrew Top

Andrew Top a la bonne idée:

1) Algorithme de Djikstra 2) Quelques heuristiques TSP.

Je recommande l'heuristique de Lin-Kernighan: c'est l'une des plus connues pour tout problème NP Complete. La seule autre chose à retenir est qu'après avoir développé à nouveau le graphique après l'étape 2, vous pouvez avoir des boucles dans votre chemin développé, vous devez donc contourner ceux-ci (regardez le degré de sommets le long de votre chemin).

Je ne sais pas vraiment à quel point cette solution sera bonne par rapport à l'optimum. Il existe probablement des cas pathologiques liés aux courts-circuits. Après tout, ce problème ressemble beaucoup à Steiner Tree: http://en.wikipedia.org/wiki/Steiner_tree et vous ne pouvez certainement pas approximer Steiner Tree en contractant simplement votre graphique et en exécutant Kruskal pour Exemple.

5
Ying Xiao

C'est pas un problème TSP et non NP-hard car la question d'origine n'exige pas que les nœuds à ne pas passer ne soient visités qu'une seule fois. Cela rend la réponse beaucoup plus simple à la force brute après avoir compilé une liste des chemins les plus courts entre tous les nœuds à passer via l'algorithme de Dijkstra. Il peut y avoir une meilleure façon de procéder, mais la plus simple serait de simplement travailler un arbre binaire à l'envers. Imaginez une liste de nœuds [début, a, b, c, fin]. Additionnez les distances simples [début-> a-> b-> c-> fin] c'est votre nouvelle distance cible à battre. Maintenant, essayez [start-> a-> c-> b-> end] et si c'est mieux définissez-le comme cible (et rappelez-vous qu'il provient de ce modèle de nœuds). Reculez sur les permutations:

  • [début-> a-> b-> c-> fin]
  • [début-> a-> c-> b-> fin]
  • [début-> b-> a-> c-> fin]
  • [début-> b-> c-> a-> fin]
  • [début-> c-> a-> b-> fin]
  • [début-> c-> b-> a-> fin]

L'une d'entre elles sera la plus courte.

(où sont les nœuds "visités plusieurs fois", le cas échéant? Ils sont juste cachés dans l'étape d'initialisation du chemin le plus court. Le chemin le plus court entre a et b peut contenir c ou même le point final. Vous n'avez pas besoin de vous en soucier )

4
bjorke

Étant donné que la quantité de nœuds et d'arêtes est relativement finie, vous pouvez probablement calculer tous les chemins possibles et prendre le plus court.

Généralement, ceci est connu sous le nom de problème des vendeurs itinérants et a un runtime polynomial non déterministe, quel que soit l'algorithme que vous utilisez.

http://en.wikipedia.org/wiki/Traveling_salesman_problem

3
Rafe

Il aurait été agréable de nous dire si l'algorithme devrait fonctionner en environ une seconde, un jour, une semaine ou plus :) . Mais s'il est intégré dans une interface utilisateur et doit être calculé plusieurs fois par jour ... un autre problème je pense.

2
Szundi

Une chose qui n'est mentionnée nulle part, est de savoir s'il est acceptable que le même sommet soit visité plus d'une fois dans le chemin. La plupart des réponses ici supposent qu'il est correct de visiter le même Edge plusieurs fois, mais ma prise en compte de la question (un chemin ne doit pas visiter le même sommet plus d'une fois!) Est qu'il est pas ok pour visiter le même sommet deux fois.

Ainsi, une approche par force brute s'appliquerait toujours, mais vous devriez supprimer les sommets déjà utilisés lorsque vous essayez de calculer chaque sous-ensemble du chemin.

0
kirsch

Que diriez-vous d'utiliser la force brute sur la douzaine de nœuds à visiter? Vous pouvez couvrir assez facilement toutes les combinaisons possibles de 12 nœuds, ce qui vous laisse un circuit optimal que vous pouvez suivre pour les couvrir.

Maintenant, votre problème est simplifié pour trouver des itinéraires optimaux du nœud de départ au circuit, que vous suivez ensuite jusqu'à ce que vous les ayez couverts, puis recherchez l'itinéraire de celui-ci à la fin.

Le chemin final est composé de:

début -> chemin d'accès au circuit * -> circuit de visite obligatoire des nœuds -> chemin d'accès à la fin * -> fin

Vous trouvez les chemins que j'ai marqués avec * comme ceci

Effectuez une recherche A * du nœud de départ à chaque point du circuit pour chacun d'entre eux, effectuez une recherche A * du nœud suivant et précédent du circuit jusqu'à la fin (car vous pouvez suivre le circuit dans les deux sens) se retrouver avec beaucoup de chemins de recherche, et vous pouvez choisir celui qui a le coût le plus bas.

Il y a beaucoup de place pour l'optimisation en mettant en cache les recherches, mais je pense que cela générera de bonnes solutions.

Cela ne va pas loin de chercher une solution optimale, car cela pourrait impliquer de quitter le circuit de visite incontournable dans la recherche.

0
justinhj