본문으로 건너뛰기
김신건의 로그

A* (A-star) 알고리즘

· 수정 · 📖 약 2분 · 761자/단어 #algorithm #search #heuristic #shortest-path
a-star, A*, A star, 에이스타, a* algorithm

정의

A* 알고리즘휴리스틱 기반 최단 경로 탐색 알고리즘. f(n)=g(n)+h(n) 을 평가 함수로 사용해 시작점에서 목표까지의 최단 경로를 찾는다. 1968년 Hart, Nilsson, Raphael이 제안.

Dijkstra의 일반화로, 휴리스틱 h(n) 이 admissible(목표까지 실제 비용 이하) 이면 최적해 보장.

문제 상황과 동기

가중치가 있는 그래프에서 s에서 t까지 최단 경로.

  • Dijkstra: 모든 방향 동등하게 확장. h(n)=0 인 A* 와 동일.
  • A*: “목표 방향” 정보를 h(n) 으로 주입. 불필요한 방향 확장 억제.

핵심 통찰: 남은 거리 예측치 h(n) 을 비용에 더하면 목표 방향으로 먼저 탐색. g(n) 은 이미 지나온 비용, h(n) 은 앞으로 남은 예측.

시각화

핵심 아이디어

open set 과 closed set 을 유지. open 에서 f(n)=g(n)+h(n) 이 가장 작은 노드를 꺼내 확장.

openSet = {start}
g[start] = 0, h[start] = heuristic(start, goal)
f[start] = g[start] + h[start]

while openSet not empty:
    current = node in openSet with smallest f
    if current == goal: 복원 후 반환
    
    openSet.remove(current)
    
    for each neighbor v of current:
        tentative_g = g[current] + w(current, v)
        if tentative_g < g[v]:
            g[v] = tentative_g
            f[v] = g[v] + heuristic(v, goal)
            openSet.insert(v)

h(n) 이 admissible(과대추정 안 함) 이면 A* 는 항상 최적 경로를 찾는다.

구현

// A*, priority queue + Manhattan heuristic (grid 예제)
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int,int>;

int heuristic(pii a, pii b) {
  return abs(a.first - b.first) + abs(a.second - b.second);
}

int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};

int astar(vector<string>& grid, pii start, pii goal) {
  int n = grid.size(), m = grid[0].size();
  vector<vector<int>> g(n, vector<int>(m, 1e9));
  priority_queue<pair<int,pii>,
                 vector<pair<int,pii>>,
                 greater<pair<int,pii>>> pq;

  g[start.first][start.second] = 0;
  int h_start = heuristic(start, goal);
  pq.push({h_start, start});

  while (!pq.empty()) {
      auto [f, cur] = pq.top(); pq.pop();
      auto [x, y] = cur;
      if (cur == goal) return g[x][y];

      for (int d = 0; d < 4; d++) {
          int nx = x + dx[d], ny = y + dy[d];
          if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
          if (grid[nx][ny] == '#') continue;
          int ng = g[x][y] + 1;
          if (ng < g[nx][ny]) {
              g[nx][ny] = ng;
              int f = ng + heuristic({nx, ny}, goal);
              pq.push({f, {nx, ny}});
          }
      }
  }
  return -1;
}

int main() {
  vector<string> grid = {
      ".....",
      ".#.#.",
      ".#.#.",
      ".#...",
      "....."
  };
  pii start = {0,0}, goal = {4,4};
  cout << astar(grid, start, goal);
  return 0;
}
stdin
..... .#.#. .#.#. .#... .....
결과
8

복잡도

항목
시간 (최선)O(E log V) - h(n) 이 목표 방향 완벽
시간 (평균)O(E log V) - typical
시간 (최악)O(V^2) - h(n) 이 나쁠 때 (Dijkstra 와 동일)
공간O(V)
최적성h(n) admissible 이면 보장

변형 / 활용

  • IDA*: 반복적 깊이 심화 A*, 메모리 적게 씀.
  • D* Lite: 동적 환경에서 경로 재계획 (로봇).
  • Theta*: any-angle pathfinding, 그리드 대각선 이동.
  • 게임 AI: NPC 길찾기 (Unity NavMesh, StarCraft).
  • 지도 내비게이션: 실제 도로망에서 A* 변형 사용.

함정

1. h(n) 이 admissible 하지 않으면 최적성 상실

과대추정 휴리스틱은 최단 경로를 놓칠 수 있다.

2. 중복 방문 관리

closed set 없이 open 만 쓰면 같은 노드를 여러 번 확장.

3. open set 이 너무 커지면 메모리 부담

격자형 맵에서 open set 수십만 개 가능. IDA* 고려.

BOJ 연습 문제

번호제목정답률링크
BOJ 1238파티 (Dijkstra)-kokoa-lab
BOJ 13549숨바꼭질 3-kokoa-lab
BOJ 17835면접보는 승범이네-kokoa-lab

참고

이 글의 용어 (3개)
너비 우선 탐색 (BFS)algorithm
정의 너비 우선 탐색 (Breadth-First Search, BFS) 는 그래프 G=(V, E) 에서 시작 정점 s 로부터 가까운 정점부터 순서대로 방문하는 알고리즘. 큐 (F…
다익스트라 알고리즘 (Dijkstra's Algorithm)algorithm
정의 다익스트라 알고리즘 (Dijkstra's Algorithm) 은 음이 아닌 가중치 그래프에서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 그리디 알고리즘. Ed…
벨만-포드 알고리즘 (Bellman-Ford Algorithm)algorithm
정의 벨만-포드 알고리즘 (Bellman-Ford Algorithm) 은 음수 가중치 간선을 허용하면서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 DP 기반 알고리…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기