시작점에서 목표 지점까지 가장 비용이 적은 경로를 찾는 알고리즘.
휴리스틱 함수는 알고리즘의 가장 중요한 부분이자 또 다른 길 찾기 알고리즘인 다익스트라 알고리즘과의 차이점이기도 하다.
A* 알고리즘은 f(n) = g(n) + h(n) 이라는 점수 계산 방식을 사용한다. 여기서
이다.
여기서 가장 적은 f(n)의 값이 가장 적은 노드를 탐색한다.
import java.util.*;
class AStarDiagonal {
static class Node {
int x, y;
int gCost; // 시작점에서 현재 노드까지의 실제 비용
int hCost; // 목표까지의 예상 비용(휴리스틱)
Node parent;
public Node(int x, int y) {
this.x = x;
this.y = y;
this.gCost = Integer.MAX_VALUE; // 시작 시 매우 큰 값으로 설정
this.hCost = 0;
this.parent = null;
}
public int getFCost() {
return gCost + hCost;
}
}
// 그리드 크기
static final int GRID_SIZE = 5;
// 8 방향 이동: 상하좌우, 대각선 4 방향
static final int[][] DIRECTIONS = {
{0, 1}, {0, -1}, {1, 0}, {-1, 0}, // 상하좌우
{1, 1}, {1, -1}, {-1, 1}, {-1, -1} // 대각선 4방향
};
// 목표까지의 휴리스틱 함수 (체스판 거리 사용)
public static int chebyshevDistance(Node a, Node b) {
return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
}
// A* 알고리즘
public static List<Node> aStar(Node start, Node goal, Node[][] grid) {
PriorityQueue<Node> openList = new PriorityQueue<>(Comparator.comparingInt(Node::getFCost));
Set<Node> closedList = new HashSet<>();
start.gCost = 0;
start.hCost = chebyshevDistance(start, goal);
openList.add(start);
while (!openList.isEmpty()) {
Node current = openList.poll();
if (current.equals(goal)) {
return constructPath(current); // 목표에 도달 시 경로 반환
}
closedList.add(current);
for (int[] direction : DIRECTIONS) {
int newX = current.x + direction[0];
int newY = current.y + direction[1];
if (isInBounds(newX, newY) && !closedList.contains(grid[newX][newY])) {
Node neighbor = grid[newX][newY];
int tentativeGCost = current.gCost + 1; // 기본 이동 비용 1
if (tentativeGCost < neighbor.gCost) {
neighbor.gCost = tentativeGCost;
neighbor.hCost = chebyshevDistance(neighbor, goal);
neighbor.parent = current;
if (!openList.contains(neighbor)) {
openList.add(neighbor);
}
}
}
}
}
return null; // 경로가 없을 경우
}
// 경로를 재구성하는 메소드
public static List<Node> constructPath(Node current) {
List<Node> path = new ArrayList<>();
while (current != null) {
path.add(current);
current = current.parent;
}
Collections.reverse(path); // 시작점부터 목표점 순으로 경로를 반환
return path;
}
// 그리드 범위 내에 있는지 확인하는 메소드
public static boolean isInBounds(int x, int y) {
return x >= 0 && x < GRID_SIZE && y >= 0 && y < GRID_SIZE;
}
public static void main(String[] args) {
// 5x5 그리드 생성
Node[][] grid = new Node[GRID_SIZE][GRID_SIZE];
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
grid[i][j] = new Node(i, j);
}
}
// 시작점과 목표점 설정
Node start = grid[0][0];
Node goal = grid[4][4];
// A* 알고리즘 실행
List<Node> path = aStar(start, goal, grid);
if (path != null) {
System.out.println("경로를 찾았습니다:");
for (Node node : path) {
System.out.println("(" + node.x + ", " + node.y + ")");
}
} else {
System.out.println("경로를 찾지 못했습니다.");
}
}
}
휴리스틱 값은 알고리즘이 효율적으로 탐색하기 위한 예상 값일 뿐이기 때문에 실제 값과 다를 수 있다.
주로 2차원 평면에서 두 지점 간의 직선 거리를 계산할 때 사용된다.
만약 노드들이 평면 위의 좌표로 표현된다면, 유클리드 거리 공식을 사용하여 추정할 수 있다.
예시: 
여기서 (x1, y1)는 현재 노드의 좌표, (x2, y2)는 목표 노드의 좌표이다.
격자 형태의 맵에서 사용할 수 있는 방법으로, 두 지점 사이의 가로, 세로 이동 거리를 합하여 추정한다.
예시:
휴리스틱 값이 0인 경우, A* 알고리즘은 다익스트라 알고리즘처럼 동작한다. 즉, 모든 경로를 탐색하지만 가장 비용이 적은 경로를 선택한다.
목표 노드까지의 거리를 전혀 추정할 수 없을 때는 휴리스틱을 0으로 설정할 수 있다.
A* 알고리즘은 g(n)과 h(n)을 결합해 효율적으로 탐색합니다.
h(n)을 잘 설계하는 것이 중요합니다.