[알고리즘] A* 알고리즘

A* (A star algorithm) 알고리즘이란??

그래프의 한 정점에서 다른 한 정점까지의 최단거리를 각각 구하는 알고리즘

목적지까지의 거리를 계산하는 휴리스틱 추정값을 이용해 다익스트라를 개선한 알고리즘휴리스틱 함수의 성능에 따라 알고리즘 성능이 달라진다.

 

시간복잡도

O(b^d) (완전 탐색) ~ O(E log V) (최적화 시)

  • b는 분기 계수 (한 노드에서 이동할 수 있는 평균 갈래 수)
  • d는 최단 경로의 깊이(시작점에서 목표까지의 단계 수)
  • E는 그래프의 간선 수, V는 노드 수

 

공간복잡도

O(b^d) (최악) ~ O(V) (일반적인 경우)

 

 

 

다익스트라와의 차이점

 

다익스트라

  • 출발점에서 모든 노드에 대한 최단거리를 탐색함
  • 단순히 현재 지나온 거리가 가장 짧은 노드를 선택해 탐색

A*

  • 출발점에서 도착점까지의 최단 거리만 탐색함
  • 휴리스틱 함수를 이용해 목표 방향으로 우선 탐색
  • 시간복잡도는 비슷하지만, 휴리스틱을 이용해 탐색하는 노드의 수가 더 적어질 수 있어서 속도가 더 빠를 수 있음

 

추천 상황

상황 추천 알고리즘
모든 노드의 최단 거리 필요 다익스트라
목표 지점까지의 최단 거리 필요 A*
가중치 없는 그래프 BFS
가중치 있는 그래프 A*

 

구현 방식

 

아래 사진은 A* 알고리즘의 기본적인 형태이다.출발 지점(초록 사각형), 도착 지점(빨간 사각형)이 있고, 각 노드에는 f, g, h라는 값을 설정해 준다.g는 출발 노드에서 현재 노드 n까지 도달하기 위한 최단 비용h는 현재 노드 n에서 목표 노드(도착지점)까지의 예상 이동 비용, 이를 휴리스틱 알고리즘으로 측정한다.

  • 자주 사용되는 휴리스틱 알고리즘은 맨해튼, 유클리드, 체비쇼프 등이 있다.

f는 g와 h를 더한 총비용이다.

 

 

 

  1. 출발 노드를 열린 목록에 넣는다.
  2. 열린 목록에서 가장 작은 f 값을 가진 노드를 꺼낸다.
  3. 해당 노드에서 갈 수 있는 노드의 f, g, h 비용을 계산하여 열린 목록에 넣어준다.
    • 해당 노드에서 갈 수 있는 노드가 이미 열린 목록에 있는 경우는 f의 값이 줄어들면 업데이트하고 아니면 그대로 둔다.
  4. 1 ~ 3을 열린 목록이 빌 때까지 수행한다. 만약 열린 목록이 비었는데 목표지점에 도착하지 못했다면 갈 수 없는 경로인 것이다.

 

 

예제

휴리스틱으로 맨헤튼을 사용하고 출발노드에서 주변 노드의 f, g, h값을 계산한 모습

 

 

예제 코드

A* 

package Path.astar;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

public class Astar {
    private AstarNode[][] map;

	// N 크기의 map 생성하고 초기화
    public void initMap(int N) {
        map = new AstarNode[N][N];
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < N; j++) {
                map[i][j] = new AstarNode(i, j);
            }
        }
    }
	
    /** A* 알고리즘으로 최단경로 찾기
    *   열린 목록을 그냥 리스트로 작성했는데 우선순위 큐(최소힙)을 사용하면 성능이 개선된다.
    *   도착점을 찾으면 부모 노드를 이용해 경로를 출력한다.
    */
    
    public void findPath(int startX, int startY, int endX, int endY) {
        List<AstarNode> openList = new ArrayList<AstarNode>();
        map[startX][startY].f = 0;
        map[startX][startY].g = 0;
        map[startX][startY].h = Heuristic(startX,startY,endX,endY);;
        openList.add(map[startX][startY]);
        int[] dx = {-1, -1, -1,1,1, 1, 0, 0};
        int[] dy = {0, -1, 1, 0, -1, 1, -1, 1};
        while(!openList.isEmpty()) {
            AstarNode node = getMinNode(openList);
            node.close = true;
            if(node.x == endX && node.y == endY) {
                System.out.println(node.f);
                while(true) {
                    System.out.println(node.x + " " + node.y);
                    node = node.parent;
                    if(node == null) {
                        break;
                    }
                }
                return;
            }
            for(int i = 0; i < dx.length; i++) {
                int newX = node.x + dx[i];
                int newY = node.y + dy[i];
                if(newX >= 0 && newX < map.length && newY >= 0 && newY < map[0].length) {
                    if(!map[newX][newY].block && !map[newX][newY].close) {
                        int cost;
                        if(Math.abs(dx[i]) + Math.abs(dy[i]) == 2)  {
                            cost = 14;
                        }
                        else cost = 10;
                        int g = node.g + cost;
                        int h = Heuristic(newX,newY,endX,endY);
                        int f = g+h;
                        if(f < map[newX][newY].f) {
                            map[newX][newY].g = g;
                            map[newX][newY].h = h;
                            map[newX][newY].f = f;
                            map[newX][newY].parent = node;
                            if(!openList.contains(map[newX][newY])) {
                                openList.add(map[newX][newY]);
                            }
                        }
                    }
                }
            }
        }
        System.out.println("no path");
    }
    // 유클리드 알고리즘
    public int Heuristic(int posX, int posY, int endX, int endY) {
        return (int) ((Math.sqrt(Math.pow(posX - endX, 2) + Math.pow(posY - endY, 2))));
    }
    // 열린목록 중에 최소 f 값을 가진 노드를 찾는 함수
    public AstarNode getMinNode(List<AstarNode> list) {
        int min = Integer.MAX_VALUE;
        AstarNode returnNode = null;
        for(AstarNode node : list) {
            if(node.f < min) {
                min = node.f;
                returnNode = node;
            }
        }
        list.remove(returnNode);
        return returnNode;
    }


}

 

AstarNode

package Path.astar;

public class AstarNode {
    int x;
    int y;
    int f;
    int g;
    int h;
    boolean block;
    boolean close;
    AstarNode parent;

    public AstarNode(int x, int y) {
        this.x = x;
        this.y = y;
        f = Integer.MAX_VALUE;
        g = 0;
        h = 0;
        parent = null;
        block = false;
        close = false;
    }
}

 

Main

package Path;


import Path.astar.Astar;

public class Main {

    public static void main(String[] args) {
        int N = 5; // 정사각현 map의 한 변 길이
        Astar astar = new Astar();
        astar.initMap(N);

        astar.findPath(0,0,4,2);

    }
}

 

실행화면

 

참고

https://recall.tistory.com/40

'알고리즘공부 > 알고리즘' 카테고리의 다른 글

[알고리즘] 벨만포드  (0) 2025.01.24
[알고리즘] 플로이드 워셜  (0) 2025.01.24
[알고리즘] 다익스트라  (0) 2025.01.24
[알고리즘] 정렬  (2) 2025.01.22
[알고리즘] 최단 경로 찾기  (0) 2025.01.20