Алгоритм A* — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Доказательство оптимальности и корректности)
(Реализация)
 
(не показана 21 промежуточная версия 7 участников)
Строка 1: Строка 1:
Алгоритм '''А*'''("A star", "А звёздочка") -- информированный  алгоритм поиска, который находит во взвешенном графе маршрут наименьшей стоимости от начальной вершины до выбранной конечной.
+
Алгоритм '''А*''' (англ. ''A star'') {{---}} алгоритм поиска, который находит во взвешенном графе маршрут наименьшей стоимости от начальной вершины до выбранной конечной.
==Эвристика==
+
==Описание==
В процессе работы алгоритма для вершин используется функция <tex>f(v) = g(v) + h(v)</tex>, где <tex>g(v)</tex> - наименьшая стоимость пути в v из стартовой вершины, <tex>h(v)</tex> - эвристическое приближение стоимости пути от v до конечной цели. <tex>h(v)</tex> должна быть эвристически допустимой, то есть не должна переоценивать рассояние до цели. Например, если наш граф является некоторой картой, разбитой сеткой, то эвристику можно назначить минимальным числом перемещений из клетки в клетку.
+
[[Файл:Astar_progress_animation.gif|right|frame|Пример работы А*. Пустые кружки принадлежат к открытому списку, а окрашенные к закрытому.]]
К примеру, если <tex>(h(v) == 0)</tex>, то А* превращается в [http://chernykh.net/images/stories/Person/deiksteira.jpg Дейкстру ]. Если <tex>h(v)</tex> всегда меньше истинной стоимости пути до цели, то А* гарантированно найдет кратчайший путь, причем чем меньше разница между эвристикой и истинной стоимостью, тем меньше вершин рассмотрит алгоритм. Если выйдет так, что эвристика превысила истинную стоимость, то А* будет работать быстрее, но возможно найдет не лучший путь, хотя его можно считать "хорошим" и если производительность предпочтительнее точности можно использовать такую эвристику.
+
В процессе работы алгоритма для вершин рассчитывается функция <tex>f(v) = g(v) + h(v)</tex>, где  
 +
*<tex>g(v)</tex> {{---}} наименьшая стоимость пути в <tex>v</tex> из стартовой вершины,  
 +
*<tex>h(v)</tex> {{---}} эвристическое приближение стоимости пути от <tex>v</tex> до конечной цели.  
  
==Псевдокод==
+
Фактически, функция <tex>f(v)</tex> {{---}} длина пути до цели, которая складывается из пройденного расстояния <tex>g(v)</tex> и оставшегося расстояния <tex>h(v)</tex>. Исходя из этого, чем меньше значение <tex>f(v)</tex>, тем раньше мы откроем вершину <tex>v</tex>, так как через неё мы предположительно достигнем расстояние до цели быстрее всего.
А* просматривает среди всех маршрутов ведущих к цели сначала те, которые благодаря имеющейся информации(эвристическая функция) в данный момент похожи на наилучший, причем алгоритм учитывает путь уже пройденный до текущей вершины.  
+
Открытые алгоритмом вершины можно хранить в очереди с приоритетом по значению <tex>f(v)</tex>. А* действует подобно [[Алгоритм Дейкстры | алгоритму Дейкстры]] и просматривает среди всех маршрутов ведущих к цели сначала те, которые благодаря имеющейся информации (эвристическая функция) в данный момент являются наилучшими.  
[[Файл:Astar_progress_animation.gif|thumb|right|Пример работы А*. Пустые кружки принадлежат к открытому списку, а окрашенные к закрытому.]]
+
<br clear="all">
void A*(start,goal)
+
==Свойства==
{
+
Чтобы A* был оптимален, выбранная функция <tex>h(v)</tex> должна быть '''допустимой''' эвристической функцией.
    closed := {}; // Множество вершин расстояние до которых мы уже оценили
+
{{Определение
    open.push(start);// Очередь с приоритетом
+
|definition=Говорят, что эвристическая оценка <tex>h(v)</tex> '''допустима''', если для любой вершины <tex>v</tex> значение <tex>h(v)</tex> меньше или равно весу кратчайшего пути от <tex>v</tex> до цели.
    f[start] = g[start] + h[start];
+
}}
    parent[start] = start;
 
    while (open.size() != 0)
 
    {
 
        x := open.pop();
 
        if (x == goal)  
 
            return succsess(x);// Кратчайший путь найден       
 
        closed.push(x); 
 
        for (y : xy in E)
 
        {
 
            if (y in closed)    
 
                continue;
 
            tmp := g[x] + d[x,y]  // Стоимость пути до y
 
            if (y not in open)
 
            {
 
                open.push(y);
 
                tentative_is_better = true;
 
            }
 
            else           
 
                if (tmp < g[y])               
 
                    tentative_is_better := true 
 
                else
 
                    tentative_is_better := false 
 
            if (tentative_is_better == true)//  
 
            {
 
                parent[y] = x;
 
                g[y] = tmp;
 
                f[y] = g[y] + h[y];
 
            }
 
        }
 
    }
 
    return failure; // Наша цель недостижима из start
 
}
 
  
==Доказательство оптимальности и корректности==
+
Допустимая оценка является оптимистичной, потому что она предполагает, что стоимость решения меньше, чем оно есть на самом деле. <br>
Полное доказательство расположено [http://www.cs.auckland.ac.nz/compsci709s2c/resources/Mike.d/astarNilsson.pdf здесь]. Здесь приведем основные факты.
+
Второе, более сильное условие {{---}} функция <tex>h(v)</tex> должна быть '''монотонной'''.
Алгоритм является допустимым(корректным) если он гарантированно находит оптимальный путь из S до целевой вершины.
 
Допустимые алгоритмы могут различаться числом рассмотренных вершин и порядком просмотра вершин.
 
Допустимость А*
 
Лемма 1. Для любой незакрытой вершины n и любого оптимального пути P из S в n существует открытая вершина n', лежащая на этом пути, для которой найденная стоимость пути из S оптимальна.
 
Теорема 1. Если для любой вершины эвристика не переоценивает настоящую стоимость минимального пути до целевой вершины, то А* допустим.
 
  
==Ссылки==
+
{{Определение
*[http://ru.wikipedia.org/wiki/Алгоритм_поиска_A* Алгоритм_поиска_A* Википедия]
+
|definition=Эвристическая функция <tex>h(v)</tex> называется '''монотонной''' (или '''преемственной'''), если для любой вершины <tex>v_1</tex> и ее потомка <tex>v_2</tex> разность <tex>h(v_1)</tex> и <tex>h(v_2)</tex> не превышает фактического веса ребра <tex>c(v_1, v_2)</tex> от <tex>v_1</tex> до <tex>v_2</tex>, а эвристическая оценка целевого состояния равна нулю.
*[http://en.wikipedia.org/wiki/A*_search_algorithm A*_search_algorithm Wikipedia]
+
}}
*[http://theory.stanford.edu/~amitp/GameProgramming/ Статья о поиске кратчайших путей и различных оптимизациях А* в частности]
+
 
*[http://dl.acm.org/citation.cfm?id=3830&coll=portal&dl=ACM Статья на ACM Digital Library]
+
{{Теорема
 +
|statement=Любая монотонная эвристика допустима, однако обратное неверно.
 +
|proof=Пусть <tex>k(v)</tex> {{---}} длина кратчайшего пути из вершины <tex>v</tex> до цели.
 +
Докажем индукцией по числу шагов до цели, что <tex>h(v) \leqslant k(v)</tex>.<br><br>
 +
Если до цели расстояние <tex>0</tex>, то <tex>v</tex> {{---}} цель и <tex>h(v) = 0 \leqslant k(v)</tex>.<br><br>
 +
Пусть <tex>v</tex> находится на расстоянии <tex>i</tex> от цели. Тогда существует потомок <tex>v'</tex>, который находится на кратчайшем пути от <tex>v</tex> до цели и <tex>v'</tex>лежит на расстоянии <tex>i - 1</tex> шагов до цели. Следовательно, <tex>h(v) \leqslant c(v, v') + h(v')</tex>. <br>
 +
По предположению, <tex>h(v') \leqslant k(v')</tex>. Следовательно, <tex>h(v) \leqslant c(v, v') + k(v') = k(v)</tex>. <br><br>Таким образом, монотонная эвристика <tex>h(v)</tex> допустима.
 +
}}
 +
 
 +
{{Утверждение
 +
|statement=Если <tex>h(v)</tex> монотонна, то последовательность значений <tex>f(v)</tex> на любом пути неубывает.
 +
|proof=Доказательство следует из определения монотонности.<br>
 +
Пусть <tex>v'</tex> {{---}} потомок <tex>v</tex>, тогда <tex>g(v') = g(v) + c(v, v')</tex>. <br>Следовательно, <tex>f(v') = g(v') + h(v') = g(v) + c(v, v') + h(v') \geqslant g(v) + h(v) = f(v)</tex>.
 +
}}
 +
 
 +
{{Утверждение
 +
|statement=Алгоритм A* является оптимальным, если функция <tex>h(v)</tex> монотонна.
 +
|proof=Последовательность вершин "развёрнутых" во время работы алгоритма находится в неубывающем порядке значений <tex>f</tex>. Поэтому очередная выбираемая вершина должна представлять собой оптимальное решение, поскольку все дальнейшие узлы будут, по меньшей мере, столь же дорогостоящими.
 +
}}
 +
 
 +
==Примеры эвристик==
 +
Поведение алгоритма сильно зависит от того, какая эвристика используется. В свою очередь, выбор эвристики зависит[[Файл:Diagonal.png|thumb|right|Пример А* на сетке с возможностью ходить в восьми напрвлениях]] от постановки задачи. Часто А* используется для моделирования перемещения по поверхности, покрытой координатной сеткой.
 +
 
 +
* Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние<ref>[https://en.wikipedia.org/wiki/Manhattan_distance Wikipedia {{---}} Manhattan distance]</ref><br> <tex>h(v) = |{v.x-goal.x}| + |{v.y-goal.y}|</tex>.
 +
 
 +
* Расстояние Чебышева<ref>[https://ru.wikipedia.org/wiki/Расстояние_Чебышева Википедия {{---}} Расстояние Чебышева]</ref> применяется, когда к четырем направлениям добавляются диагонали:<br> <tex>h(v) = \max{(|{v.x-goal.x}|, |{v.y-goal.y}|)}</tex>.
 +
 
 +
* Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой:<br> <tex>h(v) = \sqrt{(v.x-goal.x)^2 + (v.y-goal.y)^2}</tex>.
 +
 
 +
Также стоит обратить внимание на то как соотносятся <tex>f(v)</tex> и <tex>h(v)</tex>. Если они измеряются в разных величинах (например, <tex>g(v)</tex> {{---}} это расстояние в километрах, а <tex>h(v)</tex> {{---}} оценка времени пути в часах) А* может выдать некорректный результат.
 +
 
 +
==Реализация==
 +
В приведённой реализации:
 +
* <tex>Q</tex> {{---}} множество вершин, которые требуется рассмотреть,
 +
* <tex>U</tex> {{---}} множество рассмотренных вершин,
 +
* <tex>f[x]</tex> {{---}} значение эвристической функции "расстояние + стоимость" для вершины <tex>x</tex>,
 +
* <tex>g[x]</tex> {{---}} стоимость пути от начальной вершины до <tex>x</tex>,
 +
* <tex>h(x)</tex> {{---}} эвристическая оценка расстояния от вершины <tex>x</tex> до конечной вершины.
 +
На каждом этапе работы алгоритма из множества <tex>Q</tex> выбирается вершина с наименьшим значением эвристической функции и просматриваются её соседи. Для каждого из соседей обновляется расстояние, значение эвристической функции и он добавляется в множество <tex>Q</tex>.<br>
 +
Псевдокод:
 +
'''bool''' A*(start, goal)''':'''
 +
    U = <tex> \varnothing </tex>
 +
    Q = <tex> \varnothing </tex>
 +
    Q.push(start)
 +
    g[start] = 0
 +
    f[start] = g[start] + h(start)
 +
    '''while''' Q.size() != 0
 +
        current = вершина из <tex>Q</tex> с минимальным значением <tex>f</tex>
 +
        '''if''' current == goal
 +
            '''return''' ''true''                                          <font color="green">// нашли путь до нужной вершины</font>
 +
        Q.remove(current)
 +
        U.push(current)
 +
        '''for''' v : смежные с current вершины
 +
            tentativeScore = g[current] + d(current, v)          <font color="green">// d(current, v) {{---}} стоимость пути между current и v</font>
 +
            '''if''' <tex>v \in U</tex> '''and''' tentativeScore >= g[v]
 +
                '''continue'''
 +
            '''if''' <tex>v \notin U</tex> '''or''' tentativeScore < g[v]
 +
                parent[v] = current
 +
                g[v] = tentativeScore
 +
                f[v] = g[v] + h(v)
 +
                '''if''' <tex>v \notin Q</tex>
 +
                    Q.push(v)
 +
    '''return''' ''false''
 +
 
 +
==См. также==
 +
* [[Эвристики для поиска кратчайших путей]]
 +
* [[Алгоритм Флойда]]
 +
* [[Алгоритм Дейкстры]]
 +
* [[Алгоритм Форда-Беллмана]]
 +
 
 +
==Примечания==
 +
<references/>
 +
 
 +
==Источники информации==
 +
* С. Рассел, П. Норвиг {{---}} Искусственный интеллект. Современный подход, 2е издание
 +
* [https://ru.wikipedia.org/wiki/Алгоритм_поиска_A* Википедия {{---}} Алгоритм поиска A*]
 +
* [https://en.wikipedia.org/wiki/A*_search_algorithm Wikipedia {{---}} A* search algorithm]
 +
* [http://theory.stanford.edu/~amitp/GameProgramming/ Статья о поиске кратчайших путей]
 +
* [http://dl.acm.org/citation.cfm?id=3830&coll=portal&dl=ACM Generalized best-first search strategies and the optimality of A*]
  
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Кратчайшие пути в графах ]]
 
[[Категория: Кратчайшие пути в графах ]]

Текущая версия на 22:59, 6 мая 2019

Алгоритм А* (англ. A star) — алгоритм поиска, который находит во взвешенном графе маршрут наименьшей стоимости от начальной вершины до выбранной конечной.

Описание

Пример работы А*. Пустые кружки принадлежат к открытому списку, а окрашенные к закрытому.

В процессе работы алгоритма для вершин рассчитывается функция [math]f(v) = g(v) + h(v)[/math], где

  • [math]g(v)[/math] — наименьшая стоимость пути в [math]v[/math] из стартовой вершины,
  • [math]h(v)[/math] — эвристическое приближение стоимости пути от [math]v[/math] до конечной цели.

Фактически, функция [math]f(v)[/math] — длина пути до цели, которая складывается из пройденного расстояния [math]g(v)[/math] и оставшегося расстояния [math]h(v)[/math]. Исходя из этого, чем меньше значение [math]f(v)[/math], тем раньше мы откроем вершину [math]v[/math], так как через неё мы предположительно достигнем расстояние до цели быстрее всего. Открытые алгоритмом вершины можно хранить в очереди с приоритетом по значению [math]f(v)[/math]. А* действует подобно алгоритму Дейкстры и просматривает среди всех маршрутов ведущих к цели сначала те, которые благодаря имеющейся информации (эвристическая функция) в данный момент являются наилучшими.

Свойства

Чтобы A* был оптимален, выбранная функция [math]h(v)[/math] должна быть допустимой эвристической функцией.

Определение:
Говорят, что эвристическая оценка [math]h(v)[/math] допустима, если для любой вершины [math]v[/math] значение [math]h(v)[/math] меньше или равно весу кратчайшего пути от [math]v[/math] до цели.


Допустимая оценка является оптимистичной, потому что она предполагает, что стоимость решения меньше, чем оно есть на самом деле.
Второе, более сильное условие — функция [math]h(v)[/math] должна быть монотонной.


Определение:
Эвристическая функция [math]h(v)[/math] называется монотонной (или преемственной), если для любой вершины [math]v_1[/math] и ее потомка [math]v_2[/math] разность [math]h(v_1)[/math] и [math]h(v_2)[/math] не превышает фактического веса ребра [math]c(v_1, v_2)[/math] от [math]v_1[/math] до [math]v_2[/math], а эвристическая оценка целевого состояния равна нулю.


Теорема:
Любая монотонная эвристика допустима, однако обратное неверно.
Доказательство:
[math]\triangleright[/math]

Пусть [math]k(v)[/math] — длина кратчайшего пути из вершины [math]v[/math] до цели. Докажем индукцией по числу шагов до цели, что [math]h(v) \leqslant k(v)[/math].

Если до цели расстояние [math]0[/math], то [math]v[/math] — цель и [math]h(v) = 0 \leqslant k(v)[/math].

Пусть [math]v[/math] находится на расстоянии [math]i[/math] от цели. Тогда существует потомок [math]v'[/math], который находится на кратчайшем пути от [math]v[/math] до цели и [math]v'[/math]лежит на расстоянии [math]i - 1[/math] шагов до цели. Следовательно, [math]h(v) \leqslant c(v, v') + h(v')[/math].

По предположению, [math]h(v') \leqslant k(v')[/math]. Следовательно, [math]h(v) \leqslant c(v, v') + k(v') = k(v)[/math].

Таким образом, монотонная эвристика [math]h(v)[/math] допустима.
[math]\triangleleft[/math]
Утверждение:
Если [math]h(v)[/math] монотонна, то последовательность значений [math]f(v)[/math] на любом пути неубывает.
[math]\triangleright[/math]

Доказательство следует из определения монотонности.

Пусть [math]v'[/math] — потомок [math]v[/math], тогда [math]g(v') = g(v) + c(v, v')[/math].
Следовательно, [math]f(v') = g(v') + h(v') = g(v) + c(v, v') + h(v') \geqslant g(v) + h(v) = f(v)[/math].
[math]\triangleleft[/math]
Утверждение:
Алгоритм A* является оптимальным, если функция [math]h(v)[/math] монотонна.
[math]\triangleright[/math]
Последовательность вершин "развёрнутых" во время работы алгоритма находится в неубывающем порядке значений [math]f[/math]. Поэтому очередная выбираемая вершина должна представлять собой оптимальное решение, поскольку все дальнейшие узлы будут, по меньшей мере, столь же дорогостоящими.
[math]\triangleleft[/math]

Примеры эвристик

Поведение алгоритма сильно зависит от того, какая эвристика используется. В свою очередь, выбор эвристики зависит
Пример А* на сетке с возможностью ходить в восьми напрвлениях
от постановки задачи. Часто А* используется для моделирования перемещения по поверхности, покрытой координатной сеткой.
  • Если мы можем перемещаться в четырех направлениях, то в качестве эвристики стоит выбрать манхэттенское расстояние[1]
    [math]h(v) = |{v.x-goal.x}| + |{v.y-goal.y}|[/math].
  • Расстояние Чебышева[2] применяется, когда к четырем направлениям добавляются диагонали:
    [math]h(v) = \max{(|{v.x-goal.x}|, |{v.y-goal.y}|)}[/math].
  • Если передвижение не ограничено сеткой, то можно использовать евклидово расстояние по прямой:
    [math]h(v) = \sqrt{(v.x-goal.x)^2 + (v.y-goal.y)^2}[/math].

Также стоит обратить внимание на то как соотносятся [math]f(v)[/math] и [math]h(v)[/math]. Если они измеряются в разных величинах (например, [math]g(v)[/math] — это расстояние в километрах, а [math]h(v)[/math] — оценка времени пути в часах) А* может выдать некорректный результат.

Реализация

В приведённой реализации:

  • [math]Q[/math] — множество вершин, которые требуется рассмотреть,
  • [math]U[/math] — множество рассмотренных вершин,
  • [math]f[x][/math] — значение эвристической функции "расстояние + стоимость" для вершины [math]x[/math],
  • [math]g[x][/math] — стоимость пути от начальной вершины до [math]x[/math],
  • [math]h(x)[/math] — эвристическая оценка расстояния от вершины [math]x[/math] до конечной вершины.

На каждом этапе работы алгоритма из множества [math]Q[/math] выбирается вершина с наименьшим значением эвристической функции и просматриваются её соседи. Для каждого из соседей обновляется расстояние, значение эвристической функции и он добавляется в множество [math]Q[/math].
Псевдокод:

bool A*(start, goal):
    U = [math] \varnothing [/math]
    Q = [math] \varnothing [/math]
    Q.push(start)
    g[start] = 0
    f[start] = g[start] + h(start)
    while Q.size() != 0
        current = вершина из [math]Q[/math] с минимальным значением [math]f[/math]
        if current == goal
            return true                                           // нашли путь до нужной вершины
        Q.remove(current)
        U.push(current)
        for v : смежные с current вершины
            tentativeScore = g[current] + d(current, v)           // d(current, v) — стоимость пути между current и v 
            if [math]v \in U[/math] and tentativeScore >= g[v]
                continue
            if [math]v \notin U[/math] or tentativeScore < g[v]
                parent[v] = current
                g[v] = tentativeScore
                f[v] = g[v] + h(v)
                if [math]v \notin Q[/math]
                    Q.push(v)
    return false

См. также

Примечания

Источники информации