Прежде всего, используйте профилировщик . Используйте инструменты, чтобы сказать вам, что медленно; часто удивительно, что медленно. И использовать отладчик . Создайте карту с пятью узлами в ней и проходите по каждой строке кода, пытаясь найти путь. Случилось ли что-нибудь неожиданное? Если это так, выясните, что происходит.
Во-вторых, оставляя в стороне вашу проблему с производительностью, я думаю, что у вас также есть проблема с корректностью. Можете ли вы объяснить нам, почему вы считаете, что расстояние до Манхэттена является разумной эвристикой?
(Для тех, кто читает это, кто не знаком с метрикой, «расстояние Манхэттена» или «расстояние такси» - это расстояние между двумя точками, которые вам нужно пройти, если вы жили в городе, расположенном на сетке. то есть, чтобы проехать 14 миль к северо-востоку, вам нужно проехать 10 миль к северу, а затем 10 миль на восток, в общей сложности 20 миль.)
Алгоритм A * для своей корректности требует, чтобы эвристика всегда недооценивала фактическое расстояние, необходимое для перемещения между двумя точками. Если на графике есть какие-либо "диагональные" улицы, то манхэттенское расстояние переоценивает расстояние по этим путям, и поэтому алгоритм не обязательно найдет кратчайший путь .
Почему вы не используете евклидово расстояние в качестве эвристики?
Вы пробовали свой алгоритм, используя "всегда ноль" в качестве эвристики? Это гарантированно будет недооценивать. (Это дает вам реализацию алгоритма Дейкстры.)
В-третьих, вы, кажется, делаете очень много сортировки в этой реализации. Конечно, вы можете использовать приоритетную очередь; это намного дешевле, чем сортировка.
В-четвертых, у меня есть реализация A * в C # 3 в моем блоге, которую вы можете использовать; нет явных или подразумеваемых гарантий, используйте на свой страх и риск.
http://blogs.msdn.com/b/ericlippert/archive/tags/astar/
Мой код очень прост; алгоритм в моей реализации выглядит так:
var closed = new HashSet<Node>();
var queue = new PriorityQueue<double, Path<Node>>();
queue.Enqueue(0, new Path<Node>(start));
while (!queue.IsEmpty)
{
var path = queue.Dequeue();
if (closed.Contains(path.LastStep)) continue;
if (path.LastStep.Equals(destination)) return path;
closed.Add(path.LastStep);
foreach(Node n in path.LastStep.Neighbours)
{
double d = distance(path.LastStep, n);
var newPath = path.AddStep(n, d);
queue.Enqueue(newPath.TotalCost + estimate(n), newPath);
}
}
Идея состоит в том, что мы поддерживаем приоритетную очередь путей ; то есть очередь путей, которая всегда может сказать вам путь с наименьшим расстоянием. Затем мы проверяем, добрались ли мы до места назначения; Если так, мы закончили. Если нет, то мы ставим в ряд новые пути на основе их (недооценки) предполагаемого расстояния до цели.
В-пятых, этот псевдокод в Википедии может быть улучшен. Тот факт, что мой фактический код во многих отношениях легче следовать, чем псевдокод , указывает на то, что, возможно, в псевдокоде слишком много деталей.