Projeto

Geral

Perfil

Algoritmo A* » Histórico » Versão 8

Antonio de Souza Gomes Pereira, 08/03/2020 19:29 h

1 1 Antonio de Souza Gomes Pereira
# Algoritmo A*
2
3 2 Antonio de Souza Gomes Pereira
## Introdução
4
5
A* é um algoritmo para planejamento de trajetórias em um grid contendo o destino, caminhos livres e obstáculos. Sendo amplamente utilizado para encontrar o caminho mais curto (aproximação), de uma maneira rápida e inteligente. Antes de descrever o funcionamento do mesmo, começamos com o algoritmo de Dijkstra e depois como ele pode ser derivado no A*.
6
7
## Algoritmo de Dijkstra
8
9
Dado um grafo e um vértice de origem, como podemos achar a mínima distância dele para todos os outros vértices?
10
Começamos a implementação criando uma lista aberta e uma lista fechada:
11
12
* A lista fechada terá como objetivo simplesmente armazenar o estado que o vértice possui, se sua miníma distância foi calculada ou não (True/False), podendo ser simplesmente um vetor de booleans ou uma hash table. 
13
14 7 Antonio de Souza Gomes Pereira
* A lista aberta conterá todos os outros vértices que já foram visitados e não estão na lista fechada, sendo composta de um par (distância, índice). Durante a execução do algoritmo, precisaremos várias vezes saber o vértice de menor distância na lista, por isso sendo de grande utilidade utilizar uma priority queue como lista aberta, pois os elementos ficarão ordenados. 
15 2 Antonio de Souza Gomes Pereira
16
Podemos descrever o algoritmo da seguinte forma:
17
18
19
```
20
1) Initialize distances of all vertices as infinite.
21
22
2) Create an empty set.  Every item of set is a pair
23
  (weight, vertex). Weight (or distance) is used used
24
  as first item  of pair as first item is by default 
25
  used to compare two pairs.
26
27
3) Insert source vertex into the set and make its
28
   distance as 0.
29
30
4) While Set doesn't become empty, do following
31
    a) Extract minimum distance vertex from Set. 
32
       Let the extracted vertex be u.
33
    b) Loop through all adjacent of u and do 
34
       following for every vertex v.
35
36
           // If there is a shorter path to v
37
           // through u. 
38
           If dist[v] > dist[u] + weight(u, v)
39
40
               (i) Update distance of v, i.e., do
41
                     dist[v] = dist[u] + weight(u, v)
42
               (i) If v is in set, update its distance
43
                   in set by removing it first, then
44
                   inserting with new distance
45
               (ii) If v is not in set, then insert
46
                    it in set with new distance
47
               
48
5) Print distance array dist[] to print all shortest
49
   paths. 
50
```
51
52
53
54
Vamos usar o grafo abaixo como exemplo do funcionamento:
55
56
![](exgrafo.png)
57
58
A primeira coisa a se fazer é setar todas as distâncias para +INFINITO, excetuando a origem (vértice v1), que será inserida na lista aberta e terá sua distância como 0.
59
60
| LISTA ABERTA |
61
|---           |
62
|   (0 , 1)    |   
63
64
65
| LISTA FECHADA |
66
|---            |
67
68
Após isso, retiraremos o vértice de menor valor da lista aberta e o inserimos na lista fechada, nesse caso o v1, e calculamos a distância de todos os vértices adjacentes ao recém retirado, os vértices v6, v3 e v2, respectivamente com distâncias 14, 9 e 7, inserindo-os na lista aberta.
69 3 Antonio de Souza Gomes Pereira
![](grafoit1.png)
70 2 Antonio de Souza Gomes Pereira
71 1 Antonio de Souza Gomes Pereira
| LISTA ABERTA |
72 2 Antonio de Souza Gomes Pereira
|---           |
73
|   (9 , 3)    | 
74 3 Antonio de Souza Gomes Pereira
|   (13 , 2)   | 
75 2 Antonio de Souza Gomes Pereira
|   (14 , 6)   |   
76
77
78
79
| LISTA FECHADA |
80
|---            |
81
|   1           | 
82
83
    
84
85 3 Antonio de Souza Gomes Pereira
Agora devemos repetir o processo, retirando o vértice de menor valor da lista aberta e o inserindo na fechada, nesse caso o v3, e então atualizar as distâncias dos vértices adjacentes de v3. V5 não foi inicializado ainda, basta então adicioná-lo na lista aberta com a distância de 9 + 2 = 11. V2 tem, em relação a v3, distância 9 + 10 = 19, maior que o valor de 13 já existente, portanto mantemos a distância como 13. V6 tem, em relação a v3, distância 9 + 2 = 11, menor que o valor de 14 já existente, portanto substituímos o valor dele na lista aberta por 11.  
86 2 Antonio de Souza Gomes Pereira
87 1 Antonio de Souza Gomes Pereira
88 3 Antonio de Souza Gomes Pereira
![](grafoit2.png)
89 2 Antonio de Souza Gomes Pereira
90 1 Antonio de Souza Gomes Pereira
| LISTA ABERTA |
91 2 Antonio de Souza Gomes Pereira
|---           | 
92 3 Antonio de Souza Gomes Pereira
|   (11 , 5)   | 
93
|   (11 , 6)   | 
94
|   (13 , 2)   | 
95
|   (20 , 4)   | 
96
 
97 2 Antonio de Souza Gomes Pereira
  
98 1 Antonio de Souza Gomes Pereira
| LISTA FECHADA |
99
|---            |
100
|   1           | 
101 3 Antonio de Souza Gomes Pereira
|   3           | 
102 1 Antonio de Souza Gomes Pereira
103 8 Antonio de Souza Gomes Pereira
Repetimos o processo até a lista aberta estar vazia e todos os vértices estejam na lista fechada, assim encontrando as menores distâncias de cada vértice. Se quisermos a menor distância até um vértice específico, basta pararmos de iterar quando ele sair da lista aberta. Veja que não encontramos o caminho específico que leva a menor distância, para fazer isso, basta criar um vetor com os índices dos vértices que sempre é atualizado quando o valor da distância também é atualizado.
104 1 Antonio de Souza Gomes Pereira
105
## Derivando o A*
106
107 8 Antonio de Souza Gomes Pereira
O A* é uma modificação do Dijkstra que é otimizado para apenas um destino. Enquanto o Dijkstra pode encontrar caminhos para todos, o A* encontra apenas para um, priorizando os caminhos que estão levando mais próximo do objetivo, usando a distância do início e a distância estimada até o objetivo.
108 3 Antonio de Souza Gomes Pereira
A grande diferença na implementação é que a lista aberta não conterá mais o par (distância, índice), e sim o par (f, índice), sendo o parâmetro f a soma de outros dois, o g e o h.
109 1 Antonio de Souza Gomes Pereira
110 8 Antonio de Souza Gomes Pereira
* g -> A distância percorrida até chegar aquele determinado vértice, exatamente igual à distância do Dijkstra.
111 3 Antonio de Souza Gomes Pereira
* h -> A distância estimada para, a partir daquele vértice, chegar ao objetivo. Para isso podemos calcular a distância euclidiana entre os dois pontos ou usar outro tipo de métrica, como por exemplo a distância da geometria do taxista. Para fazer o cálculo, ignoramos a presença de obstáculos, por isso se tratando apenas de uma aproximação para privilegiar caminhos mais próximos do objetivo.
112
113
Segue abaixo a descrição do algoritmo:
114
115
```
116
// A* Search Algorithm
117
1.  Initialize the open list
118
2.  Initialize the closed list
119
    put the starting node on the open 
120
    list (you can leave its f at zero)
121
122
3.  while the open list is not empty
123
    a) find the node with the least f on 
124
       the open list, call it "q"
125
126
    b) pop q off the open list
127
  
128
    c) generate q's 8 successors and set their 
129
       parents to q
130
   
131
    d) for each successor
132
        i) if successor is the goal, stop search
133
          successor.g = q.g + distance between 
134
                              successor and q
135
          successor.h = distance from goal to 
136
          successor (This can be done using many 
137
          ways, we will discuss three heuristics- 
138
          Manhattan, Diagonal and Euclidean 
139
          Heuristics)
140
          
141
          successor.f = successor.g + successor.h
142
143
        ii) if a node with the same position as 
144
            successor is in the OPEN list which has a 
145
           lower f than successor, skip this successor
146
147
        iii) if a node with the same position as 
148
            successor  is in the CLOSED list which has
149
            a lower f than successor, skip this successor
150
            otherwise, add  the node to the open list
151
     end (for loop)
152
  
153
    e) push q on the closed list
154
    end (while loop) 
155
156
```
157 4 Antonio de Souza Gomes Pereira
Abaixo está um caminho gerado pelo algoritmo, considerando a distância euclideana:
158 2 Antonio de Souza Gomes Pereira
159 5 Antonio de Souza Gomes Pereira
![](gridn.png)
160 1 Antonio de Souza Gomes Pereira
161 8 Antonio de Souza Gomes Pereira
Queremos partir da célula azul e chegar na vermelha. O grid acima pode ser considerado um grafo, no qual cada célula está conectada com sua vizinha, excetuando os casos em que é um obstáculo (pintadas de cinza), onde não há conexão das células livres do grid com as pintadas de cinza.
162 5 Antonio de Souza Gomes Pereira
Considerando o custo de movimento como 1, ou seja, cada vértice possuirá g = g_pai + 1.0, se setarmos h = 0 para todo o vértice, não existiria diferença entre andar de (4,2) para (3,3) ou (4,3), e o algoritmo seria exatamente igual ao Dijkstra, porém sabemos que se mover para (3,3) leva ao destino mais rápido. 
163
Vamos quantizar o parâmetro f neste caso para determinar para onde devemos nos mover, como ambos os vértices são originados de (4,2), possuem mesmo valor de g. Para o valor de h, considerando o destino sendo (1,1), o vértice (3,3) possuirá menor distância euclideana, assim possuindo menor h e, consequentemente, menor valor de f, explicando a prioridade de movimentação para o mesmo. 
164
165
166
167 1 Antonio de Souza Gomes Pereira
## Referências
168 5 Antonio de Souza Gomes Pereira
169
https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-using-set-in-stl/
170
171
https://www.geeksforgeeks.org/a-search-algorithm/
172
173
https://medium.com/@nicholas.w.swift/easy-a-star-pathfinding-7e6689c7f7b2
174
175
http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html