설모의 기록

A* 알고리즘 본문

알고리즘

A* 알고리즘

HA_Kwon 2018. 3. 15. 14:47

최단 경로 탐색 알고리즘

 최단 경로를 탐색하는 알고리즘의 종류는 여러가지가 존재합니다. 그 종류 중 하나인 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) 을 합한 최단거리(비용)
아직 이해가 잘 되지 않아도 괜찮습니다. 이제부터 살펴볼 예시에서 자세히 알아보도록 하겠습니다.



8-puzzle 문제
 옛날에 해보신 분들도 있으실 겁니다. 8-puzzle 문제는 목표의 퍼즐에 맞게 숫자를 세팅하기 위해 한칸씩 이동시키는 게임입니다. 우선 목표 퍼즐은 아래와 같습니다.

 왼쪽에 있는 초기 상태 퍼즐을 오른쪽에 있는 목표 상태 퍼즐의 숫자들의 자리배치와 똑같이 만들어야하는 게임입니다. 이 게임에 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개의 리스트가 있습니다. 바로 openListclosedList 입니다. 평가함수 값을 검사해야 할 노드 (앞으로 방문해야 할 노드) 들은 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
Comments