Алгоритм A*: объяснение на примере и условиях оптимальности

Алгоритм A(произносится как «A-star») — широко используемый алгоритм поиска в информатике и искусственном интеллекте. Это алгоритм информированного поиска, который эффективно находит кратчайший путь между двумя узлами графа. Алгоритм Aсочетает в себе элементы алгоритма Дейкстры и жадного поиска по принципу «наилучшее качество».

Вот шаги алгоритма A*:

  1. Инициализируйте два набора: открытый и закрытый. Открытый набор содержит узлы, подлежащие оценке, а закрытый набор содержит узлы, которые уже были оценены.
  2. Назначьте ориентировочную стоимость каждому узлу. Стоимость представляет собой сумму двух частей: стоимость достижения текущего узла из начального узла (известная как g(n)) и расчетная стоимость достижения целевого узла из текущего узла (известная как h(n)). Ориентировочная стоимость обычно рассчитывается с помощью эвристической функции.
  3. Добавьте начальный узел в открытый набор.
  4. Продолжайте цикл, пока открытое множество не станет пустым:
    • Выбрать узел с наименьшей стоимостью (f(n) = g(n) + h(n)) из открытого множества. Этот узел является текущим.
    • Переместить текущий узел из открытого набора в закрытый набор.
    • Сгенерировать соседние узлы текущего узла.
    • Для каждого соседнего узла:
      • Если он уже есть в закрытом наборе, пропустите его.
      • Если его нет в открытом наборе, добавьте его в открытый набор и рассчитайте его стоимость.
      • Если он уже есть в открытом наборе, сравните новую стоимость с ранее назначенной стоимостью. Если новая стоимость ниже, обновите стоимость и обновите родительский узел.
    • Остановить цикл, если целевой узел добавлен в закрытый набор.

Условия оптимальности.
Алгоритм A* является одновременно полным и оптимальным при определенных условиях:

  1. Эвристическая функция (h(n)) никогда не должна переоценивать стоимость достижения целевого узла из текущего узла. Если h(n) допустимо, то есть никогда не завышает фактическую стоимость, то A* гарантированно найдет кратчайший путь.
  2. Искомый граф должен быть конечным и иметь неотрицательные веса ребер.
  3. Aдолжен исследовать узлы в определенном порядке. Если эвристическая функция непротиворечива (или удовлетворяет неравенству треугольника), Aтакже гарантированно будет оптимальным.

Вот пример реализации алгоритма A* в Python:

from queue import PriorityQueue
def astar(graph, start, goal, heuristic):
    open_set = PriorityQueue()
    open_set.put((0, start))
    came_from = {}
    g_score = {start: 0}
    f_score = {start: heuristic(start, goal)}
    while not open_set.empty():
        _, current = open_set.get()
        if current == goal:
            return reconstruct_path(came_from, current)
        for neighbor in graph[current]:
            tentative_g_score = g_score[current] + graph[current][neighbor]
            if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g_score
                f_score[neighbor] = g_score[neighbor] + heuristic(neighbor, goal)
                open_set.put((f_score[neighbor], neighbor))
    return None
def reconstruct_path(came_from, current):
    path = [current]
    while current in came_from:
        current = came_from[current]
        path.append(current)
    path.reverse()
    return path
# Example usage
graph = {
    'A': {'B': 5, 'C': 3},
    'B': {'D': 2},
    'C': {'D': 4, 'E': 6},
    'D': {'E': 1},
    'E': {}
}
heuristic = {
    'A': 10,
    'B': 6,
    'C': 8,
    'D': 4,
    'E': 0
}
start = 'A'
goal = 'E'
path = astar(graph, start, goal, heuristic)
print(path)  # Output: ['A', 'C', 'D', 'E']

В этом примере демонстрируется алгоритм A*, находящий кратчайший путь от узла «A» к узлу «E» в графе. Эвристическая функция предоставляется отдельно и оценивает стоимость от каждого узла до целевого узла.