일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Spring
- Algorithm
- Backtracking
- BFS
- 데이터베이스
- framework
- BAEKJOON
- Vue.js
- Vue
- springboot
- JPA
- 우아한형제들
- 웹프로그래밍
- mobx
- JavaScript
- 프레임워크
- Database
- 우아한테크캠프
- 연습문제
- 알고리즘
- TypeScript
- 백준
- SQL
- codeground
- react
- 단위테스트
- 탐색알고리즘
- DFS
- Java
- BOJ
- Today
- Total
설모의 기록
A* 알고리즘 본문
최단 경로 탐색 알고리즘
최단 경로를 탐색하는 알고리즘의 종류는 여러가지가 존재합니다. 그 종류 중 하나인 a* 알고리즘에 대해 알아보겠습니다. a* 알고리즘은 bfs 알고리즘의 한 예로, 휴리스틱(h) 값을 이용해 최단 경로를 추정해나가는 방식입니다. 아래의 설명을 보시면 아시겠지만 휴리스틱 값이 0이라고 생각하면 결국 최단 경로가 되는데요. 휴리스틱 값이 0 이라면 bfs 와 똑같은 알고리즘이 됩니다. 따라서, 이 알고리즘은 결국 bfs 알고리즘이 최단 경로를 발견한다는 것을 다시 입증해주는 예시입니다. 개념과 예시는 아래에서 설명하도록 하겠습니다.
A* 알고리즘?
한 지점에서 목표 지점으로 가는 방법 중 가장 짧은 길을 찾는 것을 최단 경로 탐색이라 하며, 지점을 노드라고 표현합니다. a* 알고리즘에서 최적의 경로는 초기 노드에서 목표 노드로 가는 경로 중 가장 최단 경로를 말합니다. 이 때, 최단 경로를 찾아내는 과정에서 휴리스틱(h) 이라는 개념을 사용합니다. 휴리스틱은 현재 노드에서 목표 노드까지 가는 최단 경로의 비용을 추정하는 것입니다. 그 값은 추정값이기 때문에 실제로 이동해보면 그 값처럼 되지 않을수도 있습니다.
a* 알고리즘은 아래의 평가함수를 통해 최적의 경로를 탐색합니다.
평가함수 f(N) 은 g(N) 과 h(N) 의 합입니다.
- g(N) : 초기 노드에서 현재 노드까지의 실제 최단거리(비용)
- h(N) : 아직 탐색하지 않았기 때문에 알 수는 없지만, 현재 노드에서 목표 노드로 가는 최단거리(비용) 추정치
- f(N) : g(N) 과 h(N) 을 합한 최단거리(비용)
왼쪽에 있는 초기 상태 퍼즐을 오른쪽에 있는 목표 상태 퍼즐의 숫자들의 자리배치와 똑같이 만들어야하는 게임입니다. 이 게임에 a* 알고리즘을 적용해볼까요?
초기 상태에서 퍼즐을 움직일 수 있는 경우의 수는 위에 보시는 것처럼 3가지 방법이 있습니다. 가운데 그림을 예로 설명하겠습니다. 가운데 그림은 숫자 8 을 움직인 모습입니다. 1칸 움직였기 때문에 g(1) = 1이고, 목표 상태에 있는 자리 배치와 같지 않는 숫자의 개수는 총 3개 (1, 2, 8) 이므로 h(1) = 3 입니다. 앞으로 자리를 수정해야 될 숫자의 개수를 h(N) 으로 생각하시면 됩니다. 따라서 f(1) = g(1) + h(1) = 4 가 되는 것입니다. 이런식으로 3가지의 경우를 모두 계산해보면 가운데의 경우가 평가함수의 값이 가장 작습니다. a* 알고리즘은 평가함수의 값이 가장 작은 곳을 따라 탐색하기 때문에 가운데의 퍼즐에서 다시 진행합니다.
가장 작은 평가함수 결과값을 가진 퍼즐에서 다시 탐색을 해보면 2가지의 경우로 퍼즐을 움직일 수 있습니다. 숫자 1과 2를 움직일 수 있지만 2는 움직였기 때문에 그 과정을 생략합니다 (아래의 코드를 보시면 아시겠지만 closedList 에 존재하는 노드는 검사하지 않습니다). 따라서 1을 움직이는 경우만 계산해보면 됩니다. 그리고 자식의 g(N) 은 부모의 g(N) 의 값과 합쳐야 한다는 거 기억해주세요! 그러면 숫자 1 을 움직였고, 부모 노드의 g 값이 1이므로 g(2) 는 2가 됩니다. 목표 상태의 자리배치에 없는 숫자들을 세어 휴리스틱 값을 계산하면 평가함수의 값이 나오겠죠? 그 값은 3이 됩니다. 이런 식으로 한 단계 더 진행해보면 목표 상태에 도달할 수 있게 됩니다. 참고로 평가함수의 결과값이 같은 퍼즐이 2개 이상 존재한다면, 이 때는 그 자식 퍼즐들을 모두 계산해본 후 그 중에서 가장 작은 평가함수 결과값을 가진 퍼즐에서 탐색을 더 진행해야 합니다.
위의 퍼즐게임에서의 최단 경로는 2 -> 1 -> 8 이 됩니다. 이제 a* 알고리즘에 대해 조금은 이해가 되시나요? 게임으로 이해해보는 것을 마무리하고 코드로 설명드리겠습니다.
코드로 적용해보기
위의 8-puzzle 예시를 보고 오셨다면 더 이해하기 편하실거예요. a* 알고리즘을 구현하는데에 필요한 2개의 리스트가 있습니다. 바로 openList 와 closedList 입니다. 평가함수 값을 검사해야 할 노드 (앞으로 방문해야 할 노드) 들은 openList에 넣습니다. 그리고 반복문을 돌며 openList 에서 노드들을 하나씩 꺼내어 그 노드가 목표 노드가 아니라면 해당 노드를 openList에서 closedList 로 옮긴 후 그 노드의 이웃 노드들을 탐색합니다. 이웃 노드가 closedList 에 존재하지 않는다면, 평가함수 값을 계산해 이전 평가함수 값보다 작으면 각 값들을 업데이트한 후 openList 에 이웃노드를 넣어줍니다. 예제 코드는 아래에 첨부하겠습니다.
참고
1. http://www.gisdeveloper.co.kr/?p=3897
2. http://itmining.tistory.com/66
3. https://ko.wikipedia.org/wiki/A*_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98
'알고리즘' 카테고리의 다른 글
최소최대 알고리즘 (Minmax Algorithm) (1) | 2018.03.21 |
---|---|
BFS 와 DFS (0) | 2018.03.21 |
[백준 1003] 피보나치 함수 (0) | 2018.01.01 |
[백준 11866] 조세퍼스 문제 (0) | 2017.12.20 |
[백준 1260] DFS 와 BFS (0) | 2017.12.20 |