web-dev-qa-db-fra.com

A * Algorithme pour les très gros graphiques, des réflexions sur la mise en cache des raccourcis?

J'écris une simulation de messagerie/logistique sur des cartes OpenStreetMap et j'ai réalisé que l'algorithme de base A * comme illustré ci-dessous ne sera pas assez rapide pour les grandes cartes (comme le Grand Londres).

http://i.imgur.com/u2tVpML.jpg

Les nœuds verts correspondent à ceux qui ont été placés dans la file d'attente ouverte/prioritaire et en raison du nombre énorme (la carte entière est de l'ordre de 1-2 millions), il faut environ 5 secondes pour trouver l'itinéraire illustré. Malheureusement, 100 ms par trajet correspondent à ma limite absolue.

Actuellement, les nœuds sont stockés à la fois dans une liste de contiguïté et dans un tableau 2D spatial 100x100.

Je cherche des méthodes où je peux échanger le temps de prétraitement, l'espace et si besoin l'optimalité de l'itinéraire, pour des requêtes plus rapides. La formule Haversine linéaire pour le coût heuristique est la fonction la plus chère selon le profileur - j'ai optimisé autant que possible mon A * de base.

Par exemple, je pensais que si je choisissais un nœud arbitraire X dans chaque quadrant du tableau 2D et exécutais A * entre chacun, je pouvais stocker les routes vers le disque pour des simulations ultérieures. Lors de l'interrogation, je peux exécuter une recherche A * uniquement dans les quadrants, pour passer de la route précalculée au X.

Existe-t-il une version plus raffinée de ce que j'ai décrit ci-dessus ou peut-être une méthode différente que je devrais suivre. Merci beaucoup!

Pour mémoire, voici quelques résultats de référence pour pondérer arbitrairement le coût heuristique et calculer le chemin entre 10 paires de nœuds choisis au hasard:

Weight // AvgDist% // Time (ms)
1       1       1461.2
1.05    1       1327.2
1.1     1       900.7
1.2     1.019658848     196.4
1.3     1.027619169     53.6
1.4     1.044714394     33.6
1.5     1.063963413     25.5
1.6     1.071694171     24.1
1.7     1.084093229     24.3
1.8     1.092208509     22
1.9     1.109188175     22.5
2       1.122856792     18.2
2.2     1.131574742     16.9
2.4     1.139104895     15.4
2.6     1.140021962     16
2.8     1.14088128      15.5
3       1.156303676     16
4       1.20256964      13
5       1.19610861      12.9

Étonnamment, l'augmentation du coefficient à 1,1 a presque réduit de moitié le temps d'exécution tout en conservant le même itinéraire.

66
drspa44

Vous devriez être en mesure de le rendre beaucoup plus rapide en échangeant l'optimalité. Voir Admissibilité et optimalité sur wikipedia.

L'idée est d'utiliser une valeur epsilon qui conduira à une solution pas pire que 1 + epsilon fois le chemin optimal, mais ce qui fera que moins de nœuds seront pris en compte par l'algorithme. Notez que cela ne signifie pas que la solution retournée sera toujours 1 + epsilon fois le chemin optimal. Ce n'est que le pire des cas. Je ne sais pas exactement comment il se comporterait dans la pratique pour votre problème, mais je pense que cela vaut la peine d'être exploré.

Vous disposez d'un certain nombre d'algorithmes qui s'appuient sur cette idée sur wikipedia. Je crois que c'est votre meilleur pari pour améliorer l'algorithme et qu'il a le potentiel de fonctionner dans votre limite de temps tout en retournant de bons chemins.

Étant donné que votre algorithme traite des millions de nœuds en 5 secondes, je suppose que vous utilisez également des tas binaires pour la mise en œuvre, n'est-ce pas? Si vous les avez implémentés manuellement, assurez-vous qu'ils sont implémentés en tant que tableaux simples et qu'ils sont des tas binaires.

23
IVlad

Il existe des algorithmes spécialisés pour ce problème qui font beaucoup de pré-calcul. De la mémoire, le pré-calcul ajoute des informations au graphique que A * utilise pour produire une heuristique beaucoup plus précise que la distance en ligne droite. Wikipedia donne les noms d'un certain nombre de méthodes à http://en.wikipedia.org/wiki/Shortest_path_problem#Road_networks et dit que Hub Labeling est le leader. Une recherche rapide à ce sujet apparaît http://research.Microsoft.com/pubs/142356/HL-TR.pdf . Un ancien, utilisant A *, se trouve à http://research.Microsoft.com/pubs/64505/goldberg-sp-wea07.pdf .

Avez-vous vraiment besoin d'utiliser Haversine? Pour couvrir Londres, j'aurais pensé que vous auriez pu supposer une terre plate et utiliser Pythagore, ou enregistrer la longueur de chaque lien dans le graphique.

9
mcdowella

Il y a un très bon article que Microsoft Research a écrit sur le sujet:

http://research.Microsoft.com/en-us/news/features/shortestpath-070709.aspx

Le document original est hébergé ici (PDF):

http://www.cc.gatech.edu/~thad/6601-gradAI-fall2012/02-search-Gutman04siam.pdf

Il y a essentiellement quelques choses que vous pouvez essayer:

  1. Commencez à la fois par la source et la destination. Cela permet de minimiser la quantité de travail gaspillé que vous effectuez lors de la traversée de la source vers la destination.
  2. Utilisez des points de repère et des autoroutes. Essentiellement, trouvez dans chaque carte des positions qui sont des chemins couramment empruntés et effectuez un pré-calcul pour déterminer comment naviguer efficacement entre ces points. Si vous pouvez trouver un chemin de votre source à un point de repère, puis à d'autres points de repère, puis à votre destination, vous pouvez rapidement trouver un itinéraire viable et optimiser à partir de là.
  3. Explorez des algorithmes comme l'algorithme "portée". Cela aide à minimiser la quantité de travail que vous ferez lors de la traversée du graphique en minimisant le nombre de sommets qui doivent être pris en compte afin de trouver un itinéraire valide.
7
mattbasta

GraphHopper fait deux choses de plus pour obtenir un routage rapide, non heuristique et flexible (note: je suis l'auteur et vous pouvez l'essayer en ligne ici )

  1. Une optimisation moins évidente consiste à éviter le mappage 1: 1 des nœuds OSM avec les nœuds internes. Au lieu de cela, GraphHopper utilise uniquement des jonctions en tant que nœuds et enregistre environ 1/8 des nœuds traversés.
  2. Il a des outils efficaces pour A *, Dijkstra ou par ex. un à plusieurs Dijkstra. Ce qui rend possible un itinéraire en moins de 1s à travers toute l'Allemagne. La version bidirectionnelle (non heuristique) de A * rend cela encore plus rapide.

Il devrait donc être possible de vous proposer des itinéraires rapides pour le Grand Londres.

De plus, le mode par défaut est le mode vitesse qui rend tout un ordre de grandeur plus rapide (par exemple 30 ms pour les routes à l'échelle européenne) mais moins flexible, car il nécessite un prétraitement ( Hiérarchies de contraction ). Si vous n'aimez pas cela, désactivez-le simplement et affinez davantage les rues incluses pour la voiture ou créez probablement un nouveau profil pour les camions, par exemple exclure les rues de service et les pistes qui devraient vous donner un coup de pouce supplémentaire de 30%. Et comme avec tout algorithme bidirectionnel, vous pouvez facilement implémenter une recherche parallèle.

5
Karussell

Je pense que cela vaut la peine d'élaborer votre idée avec des "quadrants". Plus strictement, je l'appellerais une recherche d'itinéraire en basse résolution.

Vous pouvez choisir X nœuds connectés suffisamment proches et les traiter comme un seul nœud basse résolution. Divisez votre graphique entier en ces groupes et vous obtenez un graphique basse résolution. Il s'agit d'une étape de préparation.

Afin de calculer un itinéraire de la source à la cible, identifiez d'abord les nœuds basse résolution auxquels ils appartiennent et trouvez l'itinéraire à basse résolution. Améliorez ensuite votre résultat en trouvant l'itinéraire sur le graphique haute résolution, mais en restreignant l'algorithme uniquement aux nœuds qui appartiennent aux nœuds basse résolution de l'itinéraire basse résolution (en option, vous pouvez également envisager les nœuds voisins basse résolution jusqu'à une certaine profondeur ).

Cela peut également être généralisé à plusieurs résolutions, pas seulement à haut/bas.

À la fin, vous devriez obtenir un itinéraire suffisamment proche pour être optimal. Il est localement optimal, mais peut être quelque peu pire qu'optimal globalement dans une certaine mesure, ce qui dépend du saut de résolution (c'est-à-dire de l'approximation que vous faites lorsqu'un groupe de nœuds est défini comme un seul nœud).

4
valdo

J'ai travaillé dans une grande entreprise de navigation, je peux donc affirmer avec confiance que 100 ms devraient vous permettre d'obtenir un itinéraire de Londres à Athènes, même sur un appareil embarqué. Le Grand Londres serait une carte de test pour nous, car il est commodément petit (tient facilement dans RAM - ce n'est pas réellement nécessaire)

Tout d'abord, A * est entièrement obsolète. Son principal avantage est qu'il "techniquement" ne nécessite pas de prétraitement. Dans la pratique, vous devez de toute façon prétraiter une carte OSM, ce qui est un avantage inutile.

Les drapeaux d'arc sont la principale technique pour vous donner une augmentation de vitesse énorme. Si vous divisez la carte en sections disons 5x6, vous pouvez allouer une position de 1 bit dans un entier de 32 bits pour chaque section. Vous pouvez maintenant déterminer pour chaque Edge s'il est utile en voyage to section {X,Y} d'une autre section. Très souvent, les routes sont bidirectionnelles, ce qui signifie qu'une seule des deux directions est utile. Ainsi, l'une des deux directions a ce bit défini et l'autre l'a effacé. Cela peut ne pas sembler être un réel avantage, mais cela signifie que sur de nombreuses intersections, vous réduisez le nombre de choix à considérer de 2 à seulement 1, et cela ne prend qu'une opération sur un seul bit.

3
MSalters

Il existe des dizaines de variantes A * qui peuvent convenir ici. Vous devez cependant penser à vos cas d'utilisation.

  • Êtes-vous limité en mémoire (et également en cache)?
  • Pouvez-vous paralléliser la recherche?
  • Votre implémentation de l'algorithme sera-t-elle utilisée dans un seul endroit (par exemple, le Grand Londres et non NYC ou Mumbai ou ailleurs)?

Nous n'avons aucun moyen de connaître tous les détails dont vous et votre employeur avez connaissance. Votre premier arrêt devrait donc être CiteSeer ou Google Scholar: recherchez des articles qui traitent le pathfinding avec le même ensemble général de contraintes que vous.

Sélectionnez ensuite trois ou quatre algorithmes, effectuez le prototypage, testez comment ils évoluent et affinez-les. Vous devez garder à l'esprit que vous pouvez combiner différents algorithmes dans la même grande routine d'orientation en fonction de la distance entre les points, du temps restant ou de tout autre facteur.

Comme cela a déjà été dit, sur la base de la petite échelle de votre zone cible, la suppression de Haversine est probablement votre première étape pour gagner un temps précieux sur des évaluations de trigonométres coûteuses. REMARQUE: je ne recommande pas d'utiliser la distance euclidienne en coordonnées lat, lon - reprojetez votre carte dans un exemple Mercator transverse près du centre et utilisez les coordonnées cartésiennes en yards ou en mètres!

Le précalcul est le deuxième, et changer de compilateur peut être une troisième idée évidente (passer en C ou C++ - voir https://benchmarksgame.alioth.debian.org/ pour plus de détails).

Les étapes d'optimisation supplémentaires peuvent inclure l'élimination de l'allocation dynamique de la mémoire et l'utilisation d'une indexation efficace pour la recherche parmi les nœuds (pensez à l'arbre R et à ses dérivés/alternatives).

3
Deer Hunter

Habituellement, A * s'accompagne d'une consommation de mémoire excessive plutôt que de difficultés de temps.

Cependant, je pense qu'il pourrait être utile de commencer par calculer uniquement avec des nœuds qui font partie de "grandes rues", vous choisiriez généralement une autoroute plutôt qu'une petite ruelle.

Je suppose que vous pouvez déjà l'utiliser pour votre fonction de poids, mais vous pouvez être plus rapide si vous utilisez une file d'attente prioritaire pour décider du nœud à tester ensuite pour de nouveaux déplacements.

Vous pouvez également essayer de réduire le graphique aux seuls nœuds faisant partie des bords à faible coût, puis trouver un moyen de commencer/terminer au plus proche de ces nœuds. Vous avez donc 2 chemins du début à la "grande rue" et la "grande rue" à la fin. Vous pouvez maintenant calculer le meilleur chemin entre les deux nœuds qui font partie des "grandes rues" dans un graphique réduit.

0
xuma202