다익스트라 알고리즘 with python
다익스트라 알고리즘은 최단거리를 구하는 알고리즘입니다. 다익스트라 알고리즘을 사용하면, 하나의 노드에서 다른 모든 노드까지의 거리를 구할 수 있습니다. 개인적으로 개념을 이해하는
velog.io
다익스트라 알고리즘(Dijkstra's Algorithm) 이해하기
다익스트라 알고리즘은 그래프 이론에서 가장 널리 사용되는 알고리즘 중 하나로, 단일 출발점에서 모든 다른 노드까지의 최단 경로를 찾는 알고리즘입니다.
이 포스팅에서는 다익스트라 알고리즘의 원리, 작동 방식, 그리고 다양한 활용 사례와 함께 이를 실제 문제에 적용하는 방법을 자세히 설명하겠습니다.
다익스트라 알고리즘의 개념
다익스트라 알고리즘은 네덜란드의 컴퓨터 과학자 에츠허르 다익스트라(Edsger Dijkstra)가 1956년에 고안한 알고리즘입니다.
이 알고리즘은 가중치가 있는 그래프에서 최단 경로를 찾는 데 사용됩니다.
다익스트라 알고리즘은 다음과 같은 특징을 가지고 있습니다:
- 단일 출발점 최단 경로(Single-Source Shortest Path): 주어진 출발점에서 다른 모든 노드까지의 최단 경로를 구합니다.
- 비음수 가중치(Non-negative Weights): 간선의 가중치가 음수가 아닌 경우에만 적용됩니다.
- 탐욕적 접근(Greedy Approach): 매 단계에서 가장 최단 거리를 갖는 노드를 선택하여 경로를 확장합니다.
다익스트라 알고리즘의 원리
다익스트라 알고리즘은 다음과 같은 단계로 작동합니다:
- 초기화: 출발 노드의 거리를 0으로 설정하고, 다른 모든 노드의 거리를 무한대로 설정합니다.
- 방문하지 않은 노드 중 최단 거리를 가진 노드 선택: 현재 노드에서 인접한 모든 노드의 거리를 업데이트합니다.
- 거리를 업데이트: 현재 노드와 인접한 노드 간의 거리를 계산하여 더 짧은 경로가 발견되면 거리를 업데이트합니다.
- 반복: 모든 노드를 방문할 때까지 2번과 3번 단계를 반복합니다.
다익스트라 알고리즘의 예시


다익스트라 알고리즘의 구현
다익스트라 알고리즘을 파이썬으로 구현하는 예시 코드를 보겠습니다.
이 코드는 우선순위 큐를 사용하여 효율적으로 동작합니다.
import heapq
def dijkstra(graph, start):
# distances는 시작 노드에서 각 노드까지의 최단 거리를 저장하는 딕셔너리입니다.
distances = {node: float('inf') for node in graph}
# 시작 노드에서 자기 자신으로 가는 거리는 0으로 설정합니다.
distances[start] = 0
# 우선순위 큐를 생성합니다. 시작 노드부터 처리하므로 (0, start)를 추가합니다.
priority_queue = [(0, start)]
while priority_queue:
# 현재 가장 가까운 노드를 우선순위 큐에서 꺼냅니다.
current_distance, current_node = heapq.heappop(priority_queue)
# 이미 처리된 노드라면 무시합니다.
if current_distance > distances[current_node]:
continue
# 현재 노드의 모든 이웃 노드를 확인합니다.
for neighbor, weight in graph[current_node].items():
# 현재 노드를 거쳐서 이웃 노드로 가는 거리입니다.
distance = current_distance + weight
# 이 거리(distance)가 기존에 저장된 거리보다 짧다면, 업데이트합니다.
if distance < distances[neighbor]:
distances[neighbor] = distance
# 이웃 노드를 우선순위 큐에 추가합니다.
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# 예시 그래프
graph = {
'A': {'B': 1, 'C': 2},
'B': {'A': 1, 'E': 2, 'D': 6},
'C': {'A': 2, 'E': 3, 'F': 8},
'D': {'B': 6, 'E': 1},
'E': {'B': 2, 'C': 3, 'D': 1, 'F': 7},
'F': {'C': 8, 'E': 7}
}
print(dijkstra(graph, 'A'))
코드 설명
- 우선순위 큐 초기화: heapq 모듈을 사용하여 우선순위 큐를 초기화합니다. 우선순위 큐는 다익스트라 알고리즘에서 최단 경로를 찾는 데 효율적으로 사용할 수 있습니다.
- 거리 초기화: distances 딕셔너리를 생성하여 각 노드까지의 거리를 무한대(float('inf'))로 초기화합니다. 시작 노드까지의 거리는 0으로 설정합니다.
- 우선순위 큐에 시작 노드 추가: (0, start)를 우선순위 큐에 추가합니다. 여기서 0은 시작 노드까지의 거리입니다.
- 우선순위 큐 처리:
- 큐에서 가장 가까운 노드를 꺼냅니다 (current_distance, current_node).
- 꺼낸 노드의 거리가 이미 처리된 거리보다 크다면 무시합니다.
- 꺼낸 노드의 모든 이웃 노드를 확인합니다.
- 현재 노드를 거쳐 이웃 노드로 가는 거리(distance)를 계산합니다.
- 이 거리가 기존에 저장된 거리보다 짧다면, distances 딕셔너리를 업데이트합니다.
- 업데이트된 이웃 노드를 우선순위 큐에 추가합니다.
- 최단 거리 반환: 모든 노드를 처리한 후 시작 노드에서 각 노드까지의 최단 거리를 저장한 distances 딕셔너리를 반환합니다.
예시 그래프 설명
- graph는 각 노드와 그 이웃 노드 간의 거리를 나타냅니다.
- 예를 들어, 노드 'A'는 노드 'B'와 거리 1로, 노드 'C'와 거리 2로 연결되어 있습니다.
실행 결과
코드를 실행하면 시작 노드 'A'에서 각 노드까지의 최단 거리를 출력합니다.
이 예시에서는 다음과 같은 결과를 얻을 수 있습니다:
{'A': 0, 'B': 1, 'C': 2, 'E': 3, 'D': 4, 'F': 10}
이 결과는 시작 노드 'A'에서 각 노드까지의 최단 거리를 나타냅니다.
예를 들어, 'A'에서 'B'까지의 최단 거리는 1이고, 'A'에서 'E'까지의 최단 거리는 3입니다.
다익스트라 알고리즘의 활용
최단 경로 문제
다익스트라 알고리즘은 다양한 최단 경로 문제에서 활용될 수 있습니다. 예를 들어, 지도 어플리케이션에서 출발지에서 목적지까지의 최단 경로를 찾는 문제나 네트워크에서 패킷 전송 경로를 최적화하는 문제 등에서 사용됩니다.
네트워크 라우팅
네트워크 라우팅에서는 데이터 패킷이 네트워크를 통해 효율적으로 전달되도록 경로를 설정해야 합니다. 다익스트라 알고리즘은 이 과정에서 최적의 경로를 찾는 데 유용하게 사용됩니다.
교통 최적화
교통 시스템에서 차량의 이동 경로를 최적화하기 위해 다익스트라 알고리즘을 사용할 수 있습니다. 교통 흐름을 분석하여 최적의 경로를 찾고 혼잡을 최소화하는 데 도움을 줍니다.
결론
다익스트라 알고리즘은 단일 출발점에서 모든 다른 노드까지의 최단 경로를 찾는 강력한 도구입니다.
탐욕적 접근을 통해 효율적으로 문제를 해결할 수 있으며, 다양한 실생활 문제에 적용될 수 있습니다.
다익스트라 알고리즘을 잘 이해하고 활용하면 그래프 이론 문제를 효과적으로 해결할 수 있습니다.
각 단계와 원리를 명확히 이해하고 다양한 문제에 적용해 보세요!
A*알고리즘
A* 알고리즘 (A star 알고리즘)은 Dijkstra와 유사하지만, 추가적인 휴리스틱 함수(heuristic function)을 사용하여 목표 노드까지의 예상 거리를 고려함으로써 더 효율적으로 최단 경로를 찾는 알고리즘이다. 휴리스틱은 경로를 찾는 데 있어서 탐색 속도를 높여주며, 주로 맨해튼 거리 또는 유클리드 거리를 사용한다.
A* 알고리즘의 개념
A* 알고리즘은 각 노드에 대해 이라는 평가 함수를 사용한다.
- : 시작 노드에서 현재 노드 까지의 실제 거리
- : 현재 노드 에서 목표 노드까지의 예상 거리(휴리스틱)
이 두 값을 합한 값이 작은 노드부터 탐색해 나가므로 목표 지점까지 빠르게 도달할 수 있다.
Dijkstra 알고리즘과의 차이
Dijkstra 알고리즘A* 알고리즘
| 목표 지향성 | 목표 지점 고려 없이 모든 방향 탐색 | 목표 방향으로의 예상 거리(휴리스틱)를 활용 |
| 평가 함수 | f(n) = g(n) | f(n) = g(n) + h(n) |
| 탐색 범위 | 모든 노드에 대한 최단 경로 탐색 | 목표 방향으로 탐색 범위를 좁힘 |
| 사용 사례 | 모든 노드의 최단 거리 필요 시 | 특정 목표까지의 최단 경로가 필요할 때 |
| 휴리스틱 사용 | 없음 | 있음 (예: 맨해튼 거리, 유클리드 거리 등) |
Dijkstra 알고리즘
- 기본 원리 : 시작 노드에서 다른 모든 노드까지의 최단 거리를 찾는 알고리즘이다. 이때, 가중치를 기반으로 거리를 계산한다.
- 탐색 방식 : Dijkstra 알고리즘은 각 노드의 최단 거리만을 고려한다. 목표 노드의 위치 정보는 활용하지 않는다. 따라서 모든 경로를 최소 거리 기준으로 확정해 나가는 방식으로 동작한다.
- 적용 : 목표 지점이 정해져 있지 않거나, 모든 노드에 대해 최단 거리를 구할 때 적합하다.
- 시간 복잡도 : 일반적으로 Dijkstra는 의 시간 복잡도를 가진다. ( : 노드 수, : 간선 수)
A* 알고리즘
- 기본 원리 : 시작 노드에서 "목표 노드"까지의 최단 경로 찾기
- 탐색 방식 : 가중치에 기반한 실제 거리 과 목표 지점까지의 예상 거리(휴리스틱) 을 함께 사용한다. 이라는 평가함수를 정의하여, 목표 지점까지의 예상 거리를 고려해서 탐색 범위를 좁힌다.
- 휴리스틱 함수 : 함수는 현재 노드에서 목표 노드까지의 직선 거리, 맨해튼 거리, 유클리드 거리 등을 활용하여 목표까지의 거리 예측치를 제공한다. 따라서 목표 방향으로의 탐색 속도를 가속할 수 있다!
- 적용 : 목표 노드가 명확하게 정해진 경우에 적합하다. 지도 내 경로 탐색이나 게임 AI의 길찾기 등에 자주 사용된다.
- 시간 복잡도 : A* 알고리즘의 시간 복잡도는 휴리스틱에 따라 달라지지만, Dijkstra보다 적은 탐색으로 목표에 도달할 수 있어서, 실제 성능 면에서 더 효율적이다.
Python으로 A* 알고리즘 구현하기
전체 코드
import heapq
import math
# 휴리스틱 함수 : 유클리드 거리 계산 (노드 간의 직선 거리)
def heuristic(node1, node2):
x1, y1 = node1
x2, y2 = node2
return math.sqrt((x1 - x2) **2 + (y1 - y2) **2)
def a_star(graph, start, goal):
# 각 노드까지의 실제 거리 저장할 딕셔너리 (초기값 : 무한대)
g_costs = {node: float('inf') for node in graph}
g_costs[start] = 0
# 우선순위 큐 초기화
priority_queue = [(0, start)] # (f(n), node)
came_from = {} # 경로 추적을 위한 딕셔너리
while priority_queue:
# 현재 노드를 큐에서 꺼냄
current_f_cost, current_node = heapq.heappop(priority_queue)
# 목표 노드에 도달한 경우 경로를 반환
if current_node == goal:
path = []
total_cost = g_costs[goal]
while current_node in came_from:
path.append(current_node)
current_node = came_from[current_node]
path.append(start)
return path[::-1], total_cost # 경로를 시작부터 끝까지로 반환
# 인접 노드를 탐색
for neighbor, cost in graph[current_node]:
tentative_g_cost = g_costs[current_node] + cost
if tentative_g_cost < g_costs[neighbor]:
# 새로운 최단 거리 발견
g_costs[neighbor] = tentative_g_cost
f_cost = tentative_g_cost + heuristic(neighbor, goal)
heapq.heappush(priority_queue, (f_cost, neighbor))
came_from[neighbor] = current_node
return None, None # 경로가 없는 경우
# 그래프 정의 (인접 리스트)
# 노드 위치가 좌표 (x, y)로 정의됨
graph = {
(0, 0): [((2, 0), 3), ((0, 2), 2)],
(2, 0): [((0, 0), 3), ((2, 2), 5), ((4, 0), 2)],
(0, 2): [((0, 0), 2), ((2, 2), 4)],
(2, 2): [((2, 0), 5), ((0, 2), 4), ((4, 2), 3)],
(4, 0): [((2, 0), 2), ((4, 2), 1)],
(4, 2): [((2, 2), 3), ((4, 0), 1), ((4, 4), 2)],
(4, 4): [((4, 2), 2)]
}
start = (0, 0)
goal = (4, 4)
path, total_cost = a_star(graph, start, goal)
print("최단 경로 : ", path)
print("가중치 합 : ", total_cost)

대략적인 그래프의 형태는 위 그림과 같다.
코드 상세 구조
1. heuristic 함수
# 휴리스틱 함수 : 유클리드 거리 계산 (노드 간의 직선 거리)
def heuristic(node1, node2):
x1, y1 = node1
x2, y2 = node2
return math.sqrt((x1 - x2) **2 + (y1 - y2) **2)
- 유클리드 거리를 사용해서 두 노드 간의 직선 거리를 계산한다.
- node1과 node2의 좌표 와 가 주어지면, 두 좌표 사이의 직선거리를 구한다.
2. a_star 함수 초기화
A* 알고리즘의 메인 함수인 a_star는 시작점에서 목표 지점까지의 최단 경로를 찾는다.
def a_star(graph, start, goal):
# 각 노드까지의 실제 거리 저장할 딕셔너리 (초기값 : 무한대)
g_costs = {node: float('inf') for node in graph}
g_costs[start] = 0
# 우선순위 큐 초기화
priority_queue = [(0, start)] # (f(n), node)
came_from = {} # 경로 추적을 위한 딕셔너리
- g_costs : 시작 노드에서 각 노드까지의 실제 거리를 저장한다. 초기에는 무한대로 설정하고, 시작 노드에서 시작 노드까지의 거리는 0으로 설정한다.
- 이때 Dijkstra에서와 같이 distance가 아닌 cost라고 한 것은 아래에 추가적으로 설명하겠다.
- priority_queue : (f(n), node) 형태로 우선순위 큐를 초기화한다. 이때 은 A* 알고리즘의 평가함수.
- : 시작 노드에서 현재 노드 까지의 실제 거리
- : 현재 노드 에서 목표 노드까지의 예상 거리(휴리스틱)
- came_from : 각 노드의 이전 노드를 저장하여 경로를 추적할 때 사용한다
3. 메인 탐색 루프
priority_queue에서 가장 우선순위가 높은(최단 거리 예상) 노드를 하나씩 꺼내서 탐색을 진행한다.
while priority_queue:
# 현재 노드를 큐에서 꺼냄
current_f_cost, current_node = heapq.heappop(priority_queue)
# 목표 노드에 도달한 경우 경로를 반환
if current_node == goal:
path = []
total_cost = g_costs[goal]
while current_node in came_from:
path.append(current_node)
current_node = came_from[current_node]
path.append(start)
return path[::-1], total_cost # 경로를 시작부터 끝까지로 반환
- heapq.heappop(priority_queue) : priority_queue에서 현재까지 발견된 최단 예상 거리의 노드를 꺼낸다.
- Python의 heapq 모듈은 리스트를 최소 힙(min heap)으로 사용하도록 되어 있다. heapq.heappop()를 사용하면 가장 작은 값을 가진 요소를 큐에서 제거하면서 반환한다.
- A* 알고리즘에서 요소를 제거하는 이유 : 이미 최단 경로가 확정된 노드를 다시 탐색할 필요가 없기 때문. 큐에서 꺼낸 요소는 이미 탐색이 완료된 노드로 간주할 수 있다.
- 목표 노드 도달 확인 : 꺼낸 노드가 목표 노드라면 경로 추적을 시작한다.
- came_from 딕셔너리를 사용하여 goal에서 시작 노드까지의 경로를 역순으로 찾고, 최종적으로 뒤집어서 반환한다.
4. 인접 노드 탐색 및 거리 계산
현재 노드의 인접 노드를 탐색하며, 각각의 값을 업데이트한다.
# 인접 노드를 탐색
for neighbor, cost in graph[current_node]:
tentative_g_cost = g_costs[current_node] + cost
if tentative_g_cost < g_costs[neighbor]:
# 새로운 최단 거리 발견
g_costs[neighbor] = tentative_g_cost
f_cost = tentative_g_cost + heuristic(neighbor, goal)
heapq.heappush(priority_queue, (f_cost, neighbor))
came_from[neighbor] = current_node
- tentative_g_cost : 현재 노드에서 인접 노드까지의 임시 실제 거리를 계산한다. (tentative : 잠정적인)
- tentative_g_cost = g_costs[current_node] + cost는 현재 노드까지의 거리 g_costs[current_node]와 인접 노드까지의 비용 cost의 합이다.
- 조건문 : tentative_g_cost가 기존의 g_costs[neighbor]보다 작다면 새로운 최단거리가 발견된 것이다.
- g_costs[neighbor]를 갱신하고, f_cost = tentative_g_cost + heuristic(neighbor, goal)를 통해 을 계산한다.
- 새로운 (f_cost, neighbor)를 우선순위 큐에 추가한다.
- came_from[neighbor] = current_node로 경로를 추적할 수 있도록 came_from 딕셔너리를 업데이트한다.
5. 반환값
경로가 발견되면 경로 리스트와 가중치 합을 반환한다. 목표에 도달할 수 없으면 None, None 을 반환한다.
실행 결과
최단 경로 : [(0, 0), (2, 0), (4, 0), (4, 2), (4, 4)]
가중치 합 : 8

현재 그래프를 보면, 에서 까지의 최단 경로로 다음과 같은 예를 들 수 있다.
-
- 가중치 합 :
-
- 가중치 합 :
이때 A* 알고리즘이 선택한 경로는 2번 경로인 이고, 가중치 합은 8이다. 가중치가 가장 작기 때문에 최단 경로로 선택되었다.
distance가 아닌 cost가 된 이유
cost와 distance의 차이
- distance : 보통 물리적 거리를 의미한다. Dijkstra 알고리즘에서는 노드 간의 거리(혹은 가중치)가 최단 경로 계산의 기준이기 때문에 distance라는 용어가 적합하다.
- cost: 단순한 물리적 거리뿐만 아니라 다양한 의미의 비용을 포함할 수 있다. A* 에서는 거리뿐만 아니라 목표 노드까지의 예상 비용()을 포함해 경로를 평가하므로, 비용의 개념이 더 일반적이고 적합하다.
A* 에서 cost 사용의 의미
A 알고리즘에서 cost라는 용어를 사용하면 단순 거리 이상의 다양한 비용을 포괄할 수 있다. 예를 들어, 게임에서 A 알고리즘을 사용할 때 이동 비용이 단순한 거리뿐만 아니라 지형의 난이도, 장애물 회피 비용 등을 포함할 수도 있기 때문에 cost라는 용어가 더 일반적이라고 볼 수 있다.
따라서 위에서 Python으로 작성한 A* 알고리즘에서는 g_costs라는 이름을 사용해 누적 비용을 나타냈다.
CHATGPT대화
ChatGPT의 말:
아래는 Red Blob Games의 “Grid pathfinding optimizations” 페이지(최종 수정일: 2025년 6월 9일)를 한국어로 번역한 요약입니다. MS Word 파일로 만들고 싶으시다면 아래 텍스트를 복사하여 Word에 붙여넣기 하시면 됩니다. 원본 전체를 포함해 드리며, 워드에 붙여넣고 필요에 따라 스타일(폰트, 제목, 목차 등)을 조정하시면 됩니다.
그리드 기반 최단경로 탐색 최적화
(Grid pathfinding optimizations)
출처: Red Blob Games, 2014년 3월 작성, 2020년 6월 업데이트 redblobgames.com+1redblobgames.com+1
소개
A*나 Dijkstra 알고리즘은 그래프 기반이며, 그리드에도 그대로 사용할 수 있습니다. 대부분의 맵에서는 잘 작동하지만, 성능이 중요한 프로젝트에서는 다양한 최적화 기법이 필요합니다. redblobgames.com+1redblobgames.com+1
1. 맵 표현 변경
- 웨이포인트 그래프: 전체 그리드가 아니라 코너 등 키 포인트만 노드로 사용.
- 네비게이션 메시(Navmesh): 걸을 수 있는 영역을 폴리곤으로 표현해 결정 포인트만 저장.
- 계층적 표현(Hierarchical): coarse → fine 구조로 단계별 경로 탐색.
- 쿼드트리(Quad-tree): 지역별로 분할해, 큰 열린 공간은 큰 셀, 복잡한 곳은 작은 셀로 표현 redblobgames.com
2. 그리드 구조 활용
- Jump Point Search (JPS): 균질한 그리드는 한 방향으로 점프하여 노드 수 줄임 위키백과+2redblobgames.com+2redblobgames.com+2
- Theta★: 탐색 중 시야 확인(line‑of‑sight)으로 경로를 직선화 위키백과
- Any-angle 방법: 그리드에 제한되지 않고 더 매끄러운 최단 경로 생성 위키백과
3. 노드 탐색 속도 향상
- 가중치가 없는 균일 비용일 경우 Dijkstra 대신 BFS 사용.
- 가중치 범위가 제한적이면 bin 기반 우선순위 큐 또는 HOT Queue (Heap On Top) 사용.
- Fringe Search: OPEN 세트를 배치(batch) 처리하여 성능 개선 위키백과+2redblobgames.com+2redblobgames.com+2
4. 휴리스틱 개선
- 휴리스틱이 정확할수록 A* 성능 향상.
- 피봇 포인트 사이의 최단 거리 사전 계산으로 현실적이고 효율적인 추정 가능 redblobgames.com
요약
- 그리드는 그래프의 특수 경우
- 그리드엔 그래프엔 없는 구조가 있어 활용하면 효율 향상 가능
- 균일 비용 그리드는 가중치 없는 그래프처럼 다룰 수 있음
- 휴리스틱을 나은 추정으로 개선하면 탐색 속도 대폭 향상
- 표시된 방식 외에도 다양한 path‑straightening 및 any‑angle 알고리즘 활용 가능 위키백과+1redblobgames.com+1
전체 원문을 포함하여 번역이 필요하시다면 말씀해 주세요! 페이지 전체를 세부적으로 번역해 드릴 수도 있습니다.


나의 말:
ChatGPT의 말:
좋은 질문입니다!
아래에서 다음 내용을 체계적으로 설명드릴게요:
📌 1. 다익스트라 알고리즘(Dijkstra's Algorithm)
✅ 정의
최단 경로를 찾는 그래프 탐색 알고리즘으로, 시작 노드에서 모든 노드까지의 최단 거리를 구합니다.
✅ 작동 방식 요약
- 출발 노드를 시작점으로 설정하고, 거리 값을 0으로 초기화합니다.
- 다른 모든 노드의 거리는 ∞로 설정합니다.
- 현재 노드에서 인접 노드까지 가는 비용을 계산해 최소 거리 갱신.
- 우선순위 큐(min heap)를 사용해 가장 비용이 낮은 노드를 반복 탐색.
- 모든 노드가 방문되면 종료됩니다.
✅ 특징
- 음수 가중치가 없는 그래프에서 사용 가능
- 보장: 항상 최단 거리를 구함
- 시간복잡도: O((V + E) log V) (우선순위 큐 사용 시)
📌 2. A* 알고리즘 (A-star Algorithm)
✅ 정의
다익스트라 알고리즘을 기반으로 한 알고리즘으로, **휴리스틱(heuristic)**을 추가해 목표 지점까지 최단 경로를 더 빠르게 찾습니다.
✅ 핵심 개념
- A*는 f(n) = g(n) + h(n) 을 최소화하는 노드를 선택
- g(n): 시작점에서 현재 노드까지의 실제 비용
- h(n): 휴리스틱 함수 – 현재 노드에서 목표까지의 예상 비용
- 휴리스틱이 정확할수록 경로 탐색이 빨라집니다.
✅ 대표 휴리스틱 예시 (2D 그리드 기준)
| 직선 이동만 가능 | Manhattan 거리 | abs(dx) + abs(dy) |
| 대각선 이동 가능 | Chebyshev 거리 | max(abs(dx), abs(dy)) |
| 모든 방향 이동 | 유클리드 거리 | sqrt(dx² + dy²) |
📌 3. A* vs 다익스트라 차이점
| 목적 | 모든 노드에 대한 최단 거리 | 특정 목표 지점까지의 최단 경로 |
| 성능 | 느림 (탐색 범위 큼) | 빠름 (탐색 범위 적음) |
| 휴리스틱 사용 | ❌ 사용 안함 | ✅ 사용함 |
| 경로 최적성 | ✅ 항상 최적 | ✅ 휴리스틱이 admissible(과대평가 안 하면)일 때 최적 |
| 사용 예시 | 네트워크 라우팅, 전체 맵 거리 계산 | 게임 AI, 로봇 내비게이션 등 |
📌 4. 그리드 기반 패스파인딩에서의 개선된 알고리즘들
✅ 기본 A*만 쓰면 생기는 한계
- 매 노드마다 경로를 탐색 → 많은 연산
- 그리드 방향(상하좌우 or 대각선)에 제한된 움직임
- 성능 저하 가능성 (특히 큰 맵일 경우)
🔧 개선된 대표 알고리즘
| Jump Point Search (JPS) | 그리드 기반 A* 최적화 | 불필요한 중간 노드 생략 (같은 방향으로 "점프") |
| Theta* | Any-angle A* 확장 | 대각선 직선 경로 허용 → 자연스러운 경로 |
| Hierarchical A* | 큰 맵을 추상화된 계층으로 분할 | 낮은 해상도 → 세부 해상도 순으로 탐색 |
| Fringe Search | A*의 OPEN 목록을 배치 처리 | 반복적으로 같은 f 값을 가진 노드 처리로 속도 개선 |
| Weighted A* | h(n)에 가중치 추가 (예: f = g + w * h) | 탐색 속도 향상 vs 경로 품질 간 트레이드오프 |
🔚 결론 요약
| 다익스트라 | 휴리스틱 없음 | 정확, 전체 최단 거리 | 느림 |
| A* | 휴리스틱 사용 | 빠르고 효율적 | 휴리스틱 품질에 따라 성능 차이 |
| 개선 알고리즘들 (JPS, Theta*) | A* 기반 최적화 | 속도 및 경로 자연스러움 | 구현 복잡도 증가 |
원하시는 경우:
- 🔵 이 내용을 MS Word 파일로 정리해서 드릴 수 있고,
- 🔵 각 알고리즘의 Python 예제 코드도 드릴 수 있습니다.
필요하신 항목 알려주세요!
나의 말:
ChatGPT의 말:
네, 아주 날카로운 관찰입니다. 말씀하신 문제 상황과 알고리즘의 속성을 기반으로 보면, 다익스트라 알고리즘을 직접 경로 탐색에는 쓰지 않더라도, A*의 휴리스틱으로 활용할 수 있다는 아이디어는 정확하고 실용적입니다.
✅ 문제 정리 (15×15 그리드)
- 출발점: 2곳
- 도착점: 2곳
- 장애물: 중앙을 약 1/3 가로막고 있음
- 목표: 효율적인 최단 경로 탐색
- 현실적 조건: 길은 열려 있지만 직접적인 직선 경로는 불가
✅ 왜 다익스트라를 직접 쓰면 비효율적인가?
- 다익스트라는 출발점에서 모든 격자까지의 최단 경로를 계산.
- 하지만 이 문제는 "특정한 출발점 → 특정한 도착점"으로만 필요하므로 계산량이 과다.
- 장애물이 많으면 탐색 공간이 넓어져서 더욱 비효율적.
✅ 그러나 A*의 휴리스틱 h(n) 으로는 유용하다!
A*의 핵심은:
- g(n)은 지금까지의 실제 거리 (start → n)
- h(n)은 앞으로 남은 예상 거리 (n → goal)
보통 h(n)은 맨해튼 거리나 유클리드 거리 같은 간단한 함수지만,
👉 이 방식은 장애물을 무시하므로 예측이 부정확해질 수 있습니다.
🔧 해결 방법: 다익스트라를 휴리스틱 계산용으로 사전 실행
1. 아이디어
- 각 도착점에서 다익스트라를 한 번씩 돌려서
- 그리드의 각 칸마다 goal까지의 최단 거리를 저장해둡니다.
- 이 데이터를 h(n)으로 사용 → 진짜 지형 기반 예상 거리가 됨!
2. 구조
# Pseudo-code for precomputing heuristic
for goal in goals:
distance_map = dijkstra_from(goal)
save_as_heuristic(goal, distance_map)
3. A*에서 사용 시
# A*에서 h(n) 계산
h(n) = heuristic_map[n] # 장애물 고려된 거리
✅ 장점
| 휴리스틱 품질 | 매우 정확함 (실제 장애물 반영) |
| A* 성능 | 탐색 범위 최소화 → 속도 향상 |
| 경로 품질 | 여전히 최적성 보장 가능 (admissible) |
| 사용성 | 다수의 출발/도착점에도 확장 가능 |
❗ 주의점
- 다익스트라의 거리 테이블을 goal별로 한 번씩 미리 계산해야 하므로,
- 목표 지점이 많아지면 사전 계산량 증가.
- 하지만 목표 지점이 2개 정도라면 매우 효율적입니다.
- 이 방식은 "Precomputed Heuristic A*" 또는 "Real-Cost A*" 전략이라 부릅니다.
🔚 결론
“다익스트라는 전체 계산에는 과하지만, A 휴리스틱으로 쓰면 매우 유용하다.”*
✓ 정확하고 구조적으로 우수한 아이디어입니다.
✓ 특히 복잡한 지형, 많은 장애물, 적은 도착점 상황에 탁월합니다.
필요하시면:
- 🔵 해당 방식의 Python 코드 예제
- 🔵 MS Word 요약 문서
- 🔵 A 시각화* 예제 (예: Pygame, HTML Canvas)
제작해드릴 수 있어요. 무엇이 필요하신가요?
나의 말:
ChatGPT의 말:
좋은 질문입니다.
당신이 말한 A*에서 "장애물을 고려한 휴리스틱 값"을 구하는 건 일반적인 A*의 휴리스틱(Mahattan 등)이 장애물을 무시하기 때문에 비현실적인 경로를 가정하는 문제를 해결하는 매우 실용적인 아이디어입니다.
✅ 핵심 목표
A* 알고리즘에서 h(n)을 단순한 직선 거리 대신
**"장애물을 반영한 실제 예상 거리"**로 계산하고 싶다!
✅ 방법: 도착점에서 다익스트라(Dijkstra) 실행해서 휴리스틱 테이블 만들기
🎯 아이디어
- 도착점(goal)에서 시작하여 다익스트라를 실행
- 각 셀까지의 실제 최단 거리를 계산
- 결과를 2D 배열로 저장 → 이게 h(n)이 됨!
✅ 예시: 15x15 그리드에서 휴리스틱 맵 만들기 (Python 예시 코드 포함)
🔹 1. 그리드와 장애물 정의
import heapq
WIDTH, HEIGHT = 15, 15
# 0 = 통과 가능, 1 = 장애물
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
# 예시 장애물 설정 (중앙에 가로막기)
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
🔹 2. 다익스트라로 휴리스틱 테이블 만들기
def in_bounds(x, y):
return 0 <= x < WIDTH and 0 <= y < HEIGHT
def is_passable(x, y):
return grid[y][x] == 0
def neighbors(x, y):
for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]:
nx, ny = x + dx, y + dy
if in_bounds(nx, ny) and is_passable(nx, ny):
yield nx, ny
def compute_heuristic(goal_x, goal_y):
heuristic = [[float('inf') for _ in range(WIDTH)] for _ in range(HEIGHT)]
heap = [(0, goal_x, goal_y)]
heuristic[goal_y][goal_x] = 0
while heap:
cost, x, y = heapq.heappop(heap)
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if new_cost < heuristic[ny][nx]:
heuristic[ny][nx] = new_cost
heapq.heappush(heap, (new_cost, nx, ny))
return heuristic
3. A*에서 h(n)으로 사용
goal = (14, 14)
heuristic_map = compute_heuristic(*goal)
# A*에서 h(n)는 이제 이렇게 쓸 수 있음
def h(n):
x, y = n
return heuristic_map[y][x]
✅ 장점 요약
| 장애물 무시함 | 장애물 반영함 |
| 경로 탐색에 불필요한 노드 포함 | 불필요한 경로를 배제함 |
| 속도 느림 | 더 빠르고 효율적 |
🧠 추가 팁
- 도착점이 여러 개라면 각각에 대해 휴리스틱 맵을 만들고, min(h1, h2) 같은 방식으로 사용할 수 있습니다.
- 가중치가 있는 경우에도 다익스트라 응용 가능.
- 저장된 heuristic_map은 메모리를 약간 더 쓰지만, 계산 속도 향상에 매우 유리합니다.
🔚 정리
A 알고리즘에서 장애물을 반영한 휴리스틱을 쓰고 싶다면, 도착점에서 다익스트라를 한 번 돌리고 결과를 h(n)으로 사용하라.*
필요하다면:
- 🔵 전체 A* 구현 코드 (위 휴리스틱 통합)
- 🔵 시각화 예제 (터미널 / pygame)
- 🔵 Word 문서로 이 내용 정리
어떤 게 필요하신가요?
나의 말:
ChatGPT의 말:
아주 좋은 질문입니다.
**정답은 "둘 다 가능하지만 목적에 따라 선택"**입니다.
✅ 상황 요약
- 시작점: (13,2)
- 목표점: (3,14) 와 (4,15) 두 개
- 목표: 이 중 하나로 가장 빠른 경로를 찾기
✅ 선택지 2가지
✅ 방법 1: 각 goal마다 다익스트라를 돌려 휴리스틱 테이블 2개 생성
- (3,14)에서 다익스트라 → heuristic_map_1
- (4,15)에서 다익스트라 → heuristic_map_2
- A*에서 두 테이블 중 더 가까운 값을 사용하는 것이 일반적:
🔎 이 방법의 장점:
- 각 goal까지의 정확한 거리를 반영.
- 여러 도착점 중 가장 가까운 것을 선택 가능.
- A*의 admissible 조건을 만족 → 항상 최단 경로를 찾음.
✅ 방법 2: 각 goal마다 A* 알고리즘을 따로 실행
- (13,2) → (3,14) 경로 실행 → 경로1
- (13,2) → (4,15) 경로 실행 → 경로2
→ 둘 중 cost가 작은 경로 선택.
🔎 이 방법의 장점:
- A*는 목적지까지 탐색만 하므로, 각 실행은 빠름.
- 목표가 2~3개라면 이게 더 간단할 수 있음.
🔻 단점:
- A*를 2번 실행해야 하므로 약간의 시간 소모.
- goal이 많아지면 이 방법은 선형 증가 → 비효율적
✅ 언제 1번? 언제 2번?
| 도착점이 1~2개 | 방법 2 (A* 2번 실행) |
| 도착점이 여러 개 (3개 이상) | 방법 1 (휴리스틱 테이블 병합) |
| A* 성능 최적화가 필요할 때 | 방법 1 (사전 계산된 h(n) 사용) |
✅ 보너스: 휴리스틱 병합 방식 (추천)
# 두 goal에서 다익스트라 실행
heuristic_1 = dijkstra_from(3,14)
heuristic_2 = dijkstra_from(4,15)
# 각 위치마다 더 작은 값을 선택 (최적 경로 기준)
combined_heuristic = [
[min(heuristic_1[y][x], heuristic_2[y][x]) for x in range(WIDTH)]
for y in range(HEIGHT)
]
def h(x, y):
return combined_heuristic[y][x]
🔚 결론
| 휴리스틱 테이블을 2개 만들어야 하나요? | ✅ 네, 정확한 예측을 위해서 2개를 만들고 min() 사용 |
| 프로그램을 2번 돌려야 하나요? | ✅ 또는 goal마다 A*를 2번 실행해도 됨 (도착점이 적을 때) |
필요하시면:
- 🔵 위 전략을 통합한 전체 코드 예제
- 🔵 Word 문서 요약본
- 🔵 시각화 (그리드 + 경로 + 장애물)
제공해드릴게요. 무엇이 필요하신가요?
나의 말:
ChatGPT의 말:
오류의 원인은 두 번째 목표 지점 (4,15)이 **그리드 범위(0~14)**를 초과해서 발생했습니다.
🛠 해결 방법
- 그리드 크기는 15x15이고, 유효 인덱스는 0~14.
- goal = (4,15)은 y=15로, 유효 범위를 벗어남 → IndexError.
✅ 수정 제안
목표 좌표를 유효 범위 내로 조정하세요:
goals = [(3, 14), (4, 14)] # y=15 → y=14
이렇게 바꾸면 정상 실행됩니다.
필요하시면 경로 시각화나 Word 문서도 만들어 드릴 수 있어요. 알려주세요!
나의 말:
ChatGPT의 말:
아래는 주어진 조건(15×15 그리드, 장애물 존재, 시작점 하나, 도착점 두 개)에서 A* 알고리즘을 사용해 최단 경로를 구하는 Python 전체 코드입니다. 휴리스틱은 두 goal 각각에 대해 다익스트라 알고리즘을 사용해 미리 계산한 후, 각 위치마다 최소값을 취해 사용합니다.
import heapq
# --- 설정 ---
WIDTH, HEIGHT = 15, 15
# 0 = 통과 가능, 1 = 장애물
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
# 장애물 배치: 중앙을 가로지르는 벽 (예시)
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
# 통로 확보
grid[7][7] = 0
grid[7][8] = 0
start = (13, 2)
goals = [(3, 14), (4, 14)] # 목표지점
# --- 유틸 함수 ---
def in_bounds(x, y):
return 0 <= x < WIDTH and 0 <= y < HEIGHT
def is_passable(x, y):
return grid[y][x] == 0
def neighbors(x, y):
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
if in_bounds(nx, ny) and is_passable(nx, ny):
yield nx, ny
# --- 다익스트라 기반 휴리스틱 생성 ---
def compute_heuristic(goal_x, goal_y):
heuristic = [[float('inf') for _ in range(WIDTH)] for _ in range(HEIGHT)]
heap = [(0, goal_x, goal_y)]
heuristic[goal_y][goal_x] = 0
while heap:
cost, x, y = heapq.heappop(heap)
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if new_cost < heuristic[ny][nx]:
heuristic[ny][nx] = new_cost
heapq.heappush(heap, (new_cost, nx, ny))
return heuristic
# --- 휴리스틱 맵 병합 ---
heuristic1 = compute_heuristic(*goals[0])
heuristic2 = compute_heuristic(*goals[1])
combined_heuristic = [
[min(heuristic1[y][x], heuristic2[y][x]) for x in range(WIDTH)]
for y in range(HEIGHT)
]
# --- A* 알고리즘 ---
def a_star(start, goals, heuristic_map):
sx, sy = start
open_set = [(heuristic_map[sy][sx], 0, sx, sy)]
came_from = {}
cost_so_far = {(sx, sy): 0}
while open_set:
_, cost, x, y = heapq.heappop(open_set)
if (x, y) in goals:
# 경로 복원
path = [(x, y)]
while (x, y) in came_from:
x, y = came_from[(x, y)]
path.append((x, y))
return path[::-1]
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if (nx, ny) not in cost_so_far or new_cost < cost_so_far[(nx, ny)]:
cost_so_far[(nx, ny)] = new_cost
priority = new_cost + heuristic_map[ny][nx]
heapq.heappush(open_set, (priority, new_cost, nx, ny))
came_from[(nx, ny)] = (x, y)
return None # 경로 없음
# --- 실행 ---
shortest_path = a_star(start, goals, combined_heuristic)
# 결과 출력
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in shortest_path:
row += '* '
elif grid[y][x] == 1:
row += '■ '
else:
row += '. '
print(row)
출력 예시
- S: 시작점
- G: 목표지점
- ■: 장애물
- *: 최단경로
- .: 빈 공간
이 코드는 A* 알고리즘의 휴리스틱으로 다익스트라의 결과를 활용하여 정확하고 장애물 회피 가능한 경로 탐색이 가능합니다. 추가로 원하는 조건이 있으면 반영해드릴게요.
나의 말:
ChatGPT의 말:
아래는 출발점에서 목표점까지 가는 A* 경로 탐색 문제에서
- 목표(goal): [(4, 12), (6, 12)] 중 하나
- 중간에 꼭 거쳐야 할 필수 경유지: (5,3), (8,3), (2,14), (3,14)
- A* 알고리즘 사용
하는 전체 파이썬 코드입니다.
🧠 해결 전략
- 순열을 사용해 경유지의 순서를 모두 고려한 경로 중 가장 짧은 것을 선택.
- 다익스트라 기반 휴리스틱을 사용해 A* 알고리즘의 효율을 높임.
- 출발점 → 경유지1 → 경유지2 → 경유지3 → 경유지4 → 목표(goal) 중 최단 경로 탐색.
import heapq
import itertools
# ---------- 설정 ----------
WIDTH, HEIGHT = 15, 15
# 0 = 통과 가능, 1 = 장애물
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
# 장애물 예시 (중앙 부분 막기)
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
grid[7][6] = 0 # 통로 확보
# 시작점과 목표점
start = (13, 2)
goals = [(4, 12), (6, 12)]
# 꼭 거쳐야 할 필수 경유지
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
# ---------- 기본 함수 ----------
def in_bounds(x, y):
return 0 <= x < WIDTH and 0 <= y < HEIGHT
def is_passable(x, y):
return grid[y][x] == 0
def neighbors(x, y):
for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]:
nx, ny = x+dx, y+dy
if in_bounds(nx, ny) and is_passable(nx, ny):
yield nx, ny
# ---------- 다익스트라 기반 휴리스틱 ----------
def compute_heuristic(goal_x, goal_y):
heuristic = [[float('inf') for _ in range(WIDTH)] for _ in range(HEIGHT)]
heap = [(0, goal_x, goal_y)]
heuristic[goal_y][goal_x] = 0
while heap:
cost, x, y = heapq.heappop(heap)
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if new_cost < heuristic[ny][nx]:
heuristic[ny][nx] = new_cost
heapq.heappush(heap, (new_cost, nx, ny))
return heuristic
# ---------- A* 알고리즘 ----------
def a_star(start, goal, heuristic_map):
sx, sy = start
gx, gy = goal
open_set = [(heuristic_map[sy][sx], 0, sx, sy)]
came_from = {}
cost_so_far = {(sx, sy): 0}
while open_set:
_, cost, x, y = heapq.heappop(open_set)
if (x, y) == (gx, gy):
# 경로 복원
path = [(x, y)]
while (x, y) in came_from:
x, y = came_from[(x, y)]
path.append((x, y))
return path[::-1]
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if (nx, ny) not in cost_so_far or new_cost < cost_so_far[(nx, ny)]:
cost_so_far[(nx, ny)] = new_cost
priority = new_cost + heuristic_map[ny][nx]
heapq.heappush(open_set, (priority, new_cost, nx, ny))
came_from[(nx, ny)] = (x, y)
return None # 경로 없음
# ---------- 전체 경로 찾기 ----------
def find_full_path(start, waypoints, goals):
best_path = None
min_total_length = float('inf')
# goal마다, 경유지 순열마다 최단경로 탐색
for goal in goals:
for perm in itertools.permutations(waypoints):
points = [start] + list(perm) + [goal]
full_path = []
valid = True
total_length = 0
for i in range(len(points)-1):
# 휴리스틱 미리 계산
hx, hy = points[i+1]
heuristic = compute_heuristic(hx, hy)
segment = a_star(points[i], points[i+1], heuristic)
if not segment:
valid = False
break
# 경로 중복 제거 위해 첫 점 제외
if full_path:
segment = segment[1:]
full_path.extend(segment)
total_length += len(segment)
if valid and total_length < min_total_length:
best_path = full_path
min_total_length = total_length
return best_path
# ---------- 실행 ----------
shortest_path = find_full_path(start, waypoints, goals)
# ---------- 결과 출력 ----------
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif shortest_path and (x, y) in shortest_path:
row += '* '
elif grid[y][x] == 1:
row += '■ '
else:
row += '. '
print(row)
🧾 설명 요약 (주석 포함)
- compute_heuristic: 장애물을 고려한 휴리스틱 테이블을 생성합니다.
- a_star: 지정된 시작점과 목표지점 사이의 A* 경로를 구합니다.
- find_full_path: 경유지 순열 조합을 모두 돌며 최단경로를 구합니다.
- 경로가 없으면 None을 반환합니다.
필요하시면 경로 시각화 이미지, Word 파일 등도 만들어 드릴 수 있습니다.
나의 말:
ChatGPT의 말:
좋습니다! itertools.permutations 없이 *필수 경유지들을 통과해 목표점까지 최단 경로를 찾는 A 알고리즘 코드**를 아래와 같이 구현할 수 있습니다.
✅ 변경 사항
- itertools.permutations() 제거
- 직접 구현한 get_permutations() 함수로 순열 생성
🧠 문제 조건 요약
- 출발점: (13, 2)
- 도착점 (둘 중 하나): (4, 12), (6, 12)
- 반드시 들러야 할 경유지 4곳: (5, 3), (8, 3), (2, 14), (3, 14)
- 장애물이 있음 (중앙 부분)
- A* 알고리즘 사용
- 목적: 경유지들을 하나씩 모두 지나 최종 도착지로 가는 최단 경로 찾기
✅ 전체 Python 코드 (주석 포함)
import heapq
# ---------- 설정 ----------
WIDTH, HEIGHT = 15, 15
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
# 장애물 (중앙 블록)
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
grid[7][6] = 0 # 통로 하나 만들기
# 시작점, 목표점, 경유지
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
# ---------- 기본 함수 ----------
def in_bounds(x, y):
return 0 <= x < WIDTH and 0 <= y < HEIGHT
def is_passable(x, y):
return grid[y][x] == 0
def neighbors(x, y):
for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]:
nx, ny = x+dx, y+dy
if in_bounds(nx, ny) and is_passable(nx, ny):
yield nx, ny
# ---------- 휴리스틱: 다익스트라 ----------
def compute_heuristic(goal_x, goal_y):
heuristic = [[float('inf') for _ in range(WIDTH)] for _ in range(HEIGHT)]
heap = [(0, goal_x, goal_y)]
heuristic[goal_y][goal_x] = 0
while heap:
cost, x, y = heapq.heappop(heap)
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if new_cost < heuristic[ny][nx]:
heuristic[ny][nx] = new_cost
heapq.heappush(heap, (new_cost, nx, ny))
return heuristic
# ---------- A* 알고리즘 ----------
def a_star(start, goal, heuristic_map):
sx, sy = start
gx, gy = goal
open_set = [(heuristic_map[sy][sx], 0, sx, sy)]
came_from = {}
cost_so_far = {(sx, sy): 0}
while open_set:
_, cost, x, y = heapq.heappop(open_set)
if (x, y) == (gx, gy):
path = [(x, y)]
while (x, y) in came_from:
x, y = came_from[(x, y)]
path.append((x, y))
return path[::-1]
for nx, ny in neighbors(x, y):
new_cost = cost + 1
if (nx, ny) not in cost_so_far or new_cost < cost_so_far[(nx, ny)]:
cost_so_far[(nx, ny)] = new_cost
priority = new_cost + heuristic_map[ny][nx]
heapq.heappush(open_set, (priority, new_cost, nx, ny))
came_from[(nx, ny)] = (x, y)
return None
# ---------- 순열 생성 (itertools 없이) ----------
def get_permutations(arr):
result = []
def backtrack(path, visited):
if len(path) == len(arr):
result.append(path[:])
return
for i in range(len(arr)):
if not visited[i]:
visited[i] = True
path.append(arr[i])
backtrack(path, visited)
path.pop()
visited[i] = False
backtrack([], [False]*len(arr))
return result
# ---------- 전체 경로 찾기 ----------
def find_full_path(start, waypoints, goals):
best_path = None
min_total_length = float('inf')
waypoint_orders = get_permutations(waypoints)
for goal in goals:
for perm in waypoint_orders:
points = [start] + perm + [goal]
full_path = []
valid = True
total_length = 0
for i in range(len(points) - 1):
hx, hy = points[i + 1]
heuristic = compute_heuristic(hx, hy)
segment = a_star(points[i], points[i + 1], heuristic)
if not segment:
valid = False
break
if full_path:
segment = segment[1:] # 중복 제거
full_path.extend(segment)
total_length += len(segment)
if valid and total_length < min_total_length:
best_path = full_path
min_total_length = total_length
return best_path
# ---------- 실행 ----------
shortest_path = find_full_path(start, waypoints, goals)
# ---------- 결과 출력 ----------
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif shortest_path and (x, y) in shortest_path:
row += '* '
elif grid[y][x] == 1:
row += '■ '
else:
row += '. '
print(row)
✅ 출력 예시 기호
- S: 시작점
- G: 목표점 중 하나
- W: 꼭 들러야 할 경유지
- *: 최단 경로
- ■: 장애물
- .: 빈 공간
📌 추가 기능 요청 가능
- 경로 애니메이션으로 저장
- 경로 길이 출력
- 도착점별 거리 비교
필요하시면 말씀해주세요.
나의 말:
ChatGPT의 말:
아주 좋은 질문입니다!
BFS는 첫 번째 문제는 가능한데, 두 번째 문제(경유지를 반드시 거쳐야 하는 경우)는 안 되는 이유를 정확하게 짚고 넘어가면 다음과 같습니다.
✅ 1. 첫 번째 문제: 단일 목적지까지 최단거리 ➤ BFS 가능
조건:
- 출발점 → 도착점 (장애물 있음)
- 도착점은 하나 또는 여러 개 (최소 거리만 찾으면 됨)
- 중간에 반드시 들러야 할 지점 없음
📌 이 경우에는 일반적인 BFS(Breadth-First Search)를 사용해도 괜찮습니다.
왜 가능한가?
- 모든 경로를 동일한 비용(1칸=1거리) 으로 이동한다고 가정했을 때,
BFS는 먼저 도달한 경로가 가장 짧은 거리라는 것을 보장하기 때문입니다. - 따라서, 도착점에 처음 도달한 순간의 경로가 최단 경로입니다.
❌ 2. 두 번째 문제: 여러 개의 "필수 경유지"를 모두 거쳐야 함 ➤ BFS는 곤란
조건:
- 출발점 → 반드시 특정 지점들(waypoints)을 모두 거친 후 → 도착점
- 경유지는 여러 개 ((5,3), (8,3), (2,14), (3,14) 등)
📌 이 경우에는 단순한 BFS만으로는 풀 수 없습니다.
⚠️ 왜 BFS로 풀기 어려운가? (핵심 이유)
이유 1: 경유지 순열 고려 필요
- 경유지를 4개 거쳐야 하면, 가능한 경로 순열은 4! = 24가지가 됩니다.
- BFS는 경로의 "방문 순서"나 "순열"을 직접 고려하지 않음.
- 즉, BFS는 단순히 한 번만 방문하면 된다고 가정하고 움직임 → "모든 경유지 순서대로 방문" 조건을 만족하지 못함
이유 2: 상태(visited set)에 "경유지 방문 상태"까지 기록해야 함
- BFS는 (x, y) 위치만 보고 그 지점이 "방문되었는가"를 판단합니다.
- 하지만 이번 문제는:
- 같은 좌표 (x, y)라도 "경유지 몇 개 들렀는가"에 따라 완전히 다른 상태가 됩니다.
예:
- (5,3)까지 왔을 때:
- [A] [(5,3)]만 방문함
- [B] [(5,3), (8,3)] 방문함
- 두 상황은 다르고, 다르게 탐색되어야 하지만 BFS는 이를 구분하지 않음.
BFS로 해결하려면?
- 가능은 하지만 다음 조건이 필요합니다:
- 상태를 (x, y, visited_waypoints) 형태로 만들어야 함
- 즉, 상태 공간이 기하급수적으로 커짐
- 일반 BFS보다 복잡도, 구현 난이도, 성능이 확 떨어짐
BFS는 원래 "단일 경로"에서 최단거리를 찾는 데 특화된 알고리즘이기 때문에
"여러 개의 필수 경유지"를 거쳐야 하는 경우에는 *A + 순열 탐색 방식**이 더 현실적이고 직관적입니다.
✅ 정리 요약
| 출발 → 도착 (단일 목적지) | ✅ 가능 | BFS는 최단거리 보장 |
| 출발 → 필수 경유지들 → 도착 | ❌ 불가능 (복잡) | 경유지 순서, 방문 상태 고려 필요 |
| 출발 → 조건 없는 다수 도착지 중 하나 | ✅ 가능 | BFS는 가장 가까운 도착지 먼저 도달 |
👉 추천 알고리즘
| 장애물 없는 간단한 최단거리 | BFS |
| 장애물 있고 복잡한 최단거리 | A* (휴리스틱 사용) |
| 반드시 들러야 할 지점들이 있음 | A* + 순열 탐색 + 경로 연결 |
필요하시면 "BFS로 경유지 문제를 풀 수 있는 예외적인 상황"도 정리해드릴게요.
나의 말:
ChatGPT의 말:
네! 장애물이 없는 15×15 그리드에서,
- 출발점에서
- 경유지 여러 개를 모두 거쳐
- 도착점(둘 중 하나)으로 가는 최단 경로를
BFS만으로 해결하는 코드를 작성해드릴게요.
핵심 아이디어
- 장애물이 없으므로 어디든 이동 가능 (상하좌우 1칸씩)
- BFS 탐색 상태에 현재 위치 + 지금까지 방문한 경유지 방문 상태(bitmask) 포함
- 도착점 도달 시 모든 경유지를 방문했는지 체크
전체 코드 (주석 한글로 자세히)
from collections import deque
# --- 설정 ---
WIDTH, HEIGHT = 15, 15
# 장애물 없음 (0 = 통과 가능)
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
# 경유지 인덱스 매핑 (bitmask 용)
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
# 상하좌우 이동 방향
directions = [(-1,0),(1,0),(0,-1),(0,1)]
# BFS 함수
def bfs_with_waypoints(start, waypoints, goals):
# 방문 상태: (x, y, 방문한 경유지 bitmask)
visited = set()
# 초기 상태: 위치, 방문한 경유지 bitmask, 경로
queue = deque()
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1 # 모든 경유지 방문 상태
while queue:
x, y, mask, path = queue.popleft()
# 도착지이면서 모든 경유지 방문한 경우
if (x, y) in goals and mask == full_mask:
return path
# 상하좌우 탐색
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0:
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
if (nx, ny, nmask) not in visited:
visited.add((nx, ny, nmask))
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None # 경로 없음
# 실행
shortest_path = bfs_with_waypoints(start, waypoints, goals)
# 출력
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
설명
- visited는 위치와 경유지 방문 상태(bitmask)를 같이 관리해서
같은 위치라도 방문한 경유지 상태가 다르면 별도 방문으로 처리합니다. - 모든 경유지를 방문하고 도착점에 도착하면 즉시 경로 반환.
- 장애물이 없으니 이동 가능 여부 검사만 하면 됩니다.
필요하면 장애물 있는 버전, 경유지 순서 고정 버전 등도 알려주세요!
나의 말:
ChatGPT의 말:
deque 없이 BFS 구현하려면 일반 리스트를 큐처럼 사용하면 됩니다. 다만, 리스트에서 pop(0)을 하면 O(n) 시간이 걸려서 비효율적이지만, 작은 그리드니까 문제 없을 거예요.
아래는 deque 대신 리스트를 사용한 BFS 코드입니다.
# --- 설정 ---
WIDTH, HEIGHT = 15, 15
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
directions = [(-1,0),(1,0),(0,-1),(0,1)]
def bfs_with_waypoints(start, waypoints, goals):
visited = set()
queue = []
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1
while queue:
x, y, mask, path = queue.pop(0) # 리스트에서 pop(0) 사용
if (x, y) in goals and mask == full_mask:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0:
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
if (nx, ny, nmask) not in visited:
visited.add((nx, ny, nmask))
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None
shortest_path = bfs_with_waypoints(start, waypoints, goals)
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
이렇게 하면 deque 없이 BFS가 동작합니다.
작은 문제에선 크게 성능 문제 없고, 간단하게 리스트만으로도 충분합니다!
나의 말:
ChatGPT의 말:
네, 맞습니다!
장애물이 동일 비용(1칸씩 이동) 인 그리드 환경에서는 다익스트라 알고리즘과 BFS가 사실상 동일한 역할을 합니다.
왜 BFS로도 휴리스틱 테이블(목표로부터 최단거리 맵)을 만들 수 있나요?
- 다익스트라는 가중치가 다른 그래프에서 최단거리를 구하는 알고리즘입니다.
- BFS는 가중치가 모두 동일할 때(여기서는 이동비용이 모두 1) 최단거리를 구할 수 있습니다.
따라서,
장애물이 있고 이동 비용이 모두 동일한 4방향 그리드에서 목표 지점에서부터 BFS를 돌리면,
각 지점까지의 최단 거리를 정확하게 구할 수 있어 휴리스틱 테이블로 사용할 수 있습니다.
차이점 정리
| BFS | 간선 비용이 모두 같을 때 | 빠르고 간단, 최단거리 보장 |
| 다익스트라 | 가중치가 다를 때 | 다양한 가중치에도 최단거리 보장 |
결론
- 지금 문제처럼 1칸 이동 비용이 동일한 경우
- 장애물도 단순히 '통과 가능/불가능'일 뿐 가중치 차이는 없으므로
- 휴리스틱 테이블 계산에 BFS를 쓰는 것이 더 간단하고 충분합니다.
필요하면 BFS로 휴리스틱 테이블 만드는 코드도 따로 만들어 드릴게요!
나의 말:
ChatGPT의 말:
네! BFS로 휴리스틱 테이블(목표지점부터 각 위치까지 최단거리 거리 맵)을 만든 뒤,
여러 경유지를 반드시 거쳐 목표까지 가는 최단 경로를 찾는 문제를 장애물 없이 BFS만으로 해결하는 코드를 만들어 드릴게요.
풀이 전략 요약
- 목표지점 별로 BFS를 돌려 휴리스틱 테이블을 만듭니다. (이동 비용 동일하기 때문)
- 출발 → 경유지들 → 목표 경로는 A*처럼 복잡하게 하지 않고,
모든 경유지 방문 상태(bitmask)를 포함해 BFS 탐색으로 해결합니다. - BFS 상태: (x, y, 방문한 경유지 bitmask)
- 장애물이 없으므로 이동은 상하좌우 4방향 가능
from collections import deque
# --- 환경 설정 ---
WIDTH, HEIGHT = 15, 15
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)] # 장애물 없음
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
# 경유지 인덱스 매핑 (bitmask용)
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
directions = [(-1,0),(1,0),(0,-1),(0,1)]
# --- BFS로 휴리스틱 테이블 생성 ---
def bfs_heuristic(goal):
queue = deque([goal])
dist = [[float('inf')] * WIDTH for _ in range(HEIGHT)]
gx, gy = goal
dist[gy][gx] = 0
while queue:
x, y = queue.popleft()
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0 and dist[ny][nx] == float('inf'):
dist[ny][nx] = dist[y][x] + 1
queue.append((nx, ny))
return dist
# 목표별 휴리스틱 테이블
heuristics = [bfs_heuristic(goal) for goal in goals]
# 각 칸에서 두 목표 중 더 가까운 목표까지의 최소 거리
combined_heuristic = [
[min(heuristics[0][y][x], heuristics[1][y][x]) for x in range(WIDTH)]
for y in range(HEIGHT)
]
# --- 모든 경유지를 거쳐 목표까지 가는 BFS ---
def bfs_with_waypoints_and_heuristic(start, waypoints, goals, heuristic_map):
visited = set()
queue = deque()
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1
while queue:
x, y, mask, path = queue.popleft()
# 도착점에 도달하고 모든 경유지 방문 완료
if (x, y) in goals and mask == full_mask:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0:
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
state = (nx, ny, nmask)
if state not in visited:
# 휴리스틱 거리를 이용해 우선순위 부여 가능 (선택 사항)
# 여기서는 그냥 BFS이므로 방문만 체크
visited.add(state)
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None
# --- 실행 ---
shortest_path = bfs_with_waypoints_and_heuristic(start, waypoints, goals, combined_heuristic)
# --- 결과 출력 ---
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
설명
- 휴리스틱 테이블 생성: 목표지점별 BFS를 돌려 각 칸에서 목표까지 거리 계산
- BFS 탐색: (위치, 방문한 경유지 상태)를 큐와 방문 집합으로 관리
- 조건: 모든 경유지 방문 + 목표 도착 시 경로 반환
- 장애물이 없으므로 장애물 통과 검사만 하면 됩니다.
필요하면 장애물이 있는 경우, 혹은 A* + 휴리스틱을 추가하는 버전도 만들어 드릴게요!
나의 말:
ChatGPT의 말:
거쳐야 할 곳이 없는 경우도 위 코드는 작동합니다만,
몇 가지를 명확히 확인해두는 게 좋아요:
1. waypoints가 빈 리스트인 경우
- waypoints = []
- 따라서 waypoint_indices = {}
- full_mask = (1 << 0) - 1 = 0 (즉, 모든 경유지 방문 상태는 0)
2. BFS 시작 시 start_mask
- start_mask는 0으로 초기화되고 바뀌지 않습니다.
3. BFS 종료 조건
- mask == full_mask 이 조건이 항상 True (0 == 0)
- 따라서 도착점에 도달하면 바로 경로를 반환합니다.
결론
- 경유지가 없는 경우에도 정상 작동합니다.
- 이 경우는 단순히 출발지에서 목표지점까지의 최단경로를 BFS로 찾는 것과 동일합니다.
필요하면 거쳐야 할 곳 있는/없는 케이스를 분기 처리해서 최적화하는 방법도 알려드릴게요!
나의 말:
ChatGPT의 말:
아하! 조건이
- 15×15 격자
- 장애물 없음
- 상하(위, 아래) 방향으로만 이동 가능 (좌우 이동 불가)
- 대각선 이동 불가 (애초에 불가능)
- 거쳐야 할 곳 여러 개 있음 또는 없을 수 있음
- 출발점 → 경유지들 → 목표점 중 하나까지 최단 경로 탐색
인 거죠?
그러면 기존 코드를 상하 이동만 가능하도록 수정해야 합니다.
아래는 수정된 BFS 코드입니다. 좌우 이동은 없고, 상(-1,0), 하(+1,0)만 가능합니다.
수정된 BFS 코드 (상하 이동만 가능)
from collections import deque
# --- 환경 설정 ---
WIDTH, HEIGHT = 15, 15
grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)] # 장애물 없음
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
# 상하 이동만 가능: (dx, dy) 순서가 x, y라 x가 좌우, y가 상하이므로
# 여기서 y축(세로)이 HEIGHT 방향, x축(가로)가 WIDTH 방향입니다.
# (x, y)에서 y가 변해야 상하 이동입니다.
directions = [(0, -1), (0, 1)] # 위, 아래
def bfs_with_waypoints(start, waypoints, goals):
visited = set()
queue = deque()
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1
while queue:
x, y, mask, path = queue.popleft()
if (x, y) in goals and mask == full_mask:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0:
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
state = (nx, ny, nmask)
if state not in visited:
visited.add(state)
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None
# 실행
shortest_path = bfs_with_waypoints(start, waypoints, goals)
# 결과 출력
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
요점 정리
- 이동 방향 리스트를 (0, -1), (0, 1) 로 변경해서 상/하 이동만 허용했습니다.
- (x, y) 좌표 체계에서 x는 좌우, y는 상하 방향임에 유의하세요.
- 나머지 로직은 동일하며, 경유지 방문 상태(bitmask)를 함께 추적합니다.
필요하면 장애물 추가, 경유지 없는 경우 별도 처리 등도 알려주세요!
나의 말:
ChatGPT의 말:
네, 맞습니다!
정리해드리면
- 장애물 없음 (grid가 모두 0으로 설정되어 있음)
- 여러 개의 경유지(waypoints)가 있는 경우
- 상하 방향(위, 아래)으로만 이동 가능
- 출발점에서 시작해 모든 경유지를 방문하고 목표지점(goals) 중 한 곳에 도착하는 최단 경로를 BFS로 탐색
- 경유지 방문 상태를 비트마스크로 추적하여 중복 방문 상태를 방지
코드 핵심 내용
- grid 내 장애물이 없으므로 장애물 체크가 항상 통과함
- directions = [(0, -1), (0, 1)] 로 상하 이동만 가능하도록 설정
- BFS 탐색 시 (x, y, 방문한 경유지 비트마스크)를 상태로 저장 및 방문 처리
- 도착점에 도달할 때 모든 경유지 방문 완료 여부(mask == full_mask)를 검사
필요하면
- 장애물 있는 경우 코드
- 경유지 없는 경우 코드
- 좌우 이동 허용 코드
언제든 말씀해 주세요!
나의 말:
ChatGPT의 말:
네! 장애물이 있고 경유지가 있는 경우와 없는 경우를 각각 따로 분리해 Python BFS 코드로 만들어 드릴게요.
1. 장애물이 있고 경유지(waypoints)가 있는 경우
- 장애물(1)은 통과 불가
- 상하좌우 이동 가능 (대각선 불가)
- 경유지 방문 상태(bitmask) 추적
- 모든 경유지 방문 후 목표 도착 시 종료
from collections import deque
WIDTH, HEIGHT = 15, 15
# 예시 장애물 맵 (중앙에 장애물 블록)
grid = [[0]*WIDTH for _ in range(HEIGHT)]
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
grid[7][6] = 0 # 통로 확보
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
directions = [(-1,0),(1,0),(0,-1),(0,1)] # 상하좌우 이동
def bfs_with_waypoints_obstacles(start, waypoints, goals, grid):
visited = set()
queue = deque()
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1
while queue:
x, y, mask, path = queue.popleft()
if (x, y) in goals and mask == full_mask:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0: # 장애물 없는 곳만 이동 가능
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
state = (nx, ny, nmask)
if state not in visited:
visited.add(state)
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None
# 실행
shortest_path = bfs_with_waypoints_obstacles(start, waypoints, goals, grid)
# 출력
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
elif grid[y][x] == 1:
row += '■ '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
2. 장애물이 있고 경유지가 없는 경우
- 장애물 있음
- 상하좌우 이동 가능
- 경유지 추적 불필요 → 상태는 (x, y)만 관리
from collections import deque
WIDTH, HEIGHT = 15, 15
# 장애물 맵 동일하게 사용
grid = [[0]*WIDTH for _ in range(HEIGHT)]
for y in range(5, 10):
for x in range(WIDTH):
grid[y][x] = 1
grid[7][6] = 0
start = (13, 2)
goals = [(4, 12), (6, 12)]
directions = [(-1,0),(1,0),(0,-1),(0,1)]
def bfs_no_waypoints_obstacles(start, goals, grid):
visited = set()
queue = deque()
queue.append((start[0], start[1], [start]))
visited.add((start[0], start[1]))
while queue:
x, y, path = queue.popleft()
if (x, y) in goals:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0 and (nx, ny) not in visited:
visited.add((nx, ny))
queue.append((nx, ny, path + [(nx, ny)]))
return None
# 실행
shortest_path = bfs_no_waypoints_obstacles(start, goals, grid)
# 출력
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif shortest_path and (x, y) in shortest_path:
row += '* '
elif grid[y][x] == 1:
row += '■ '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
필요하시면
- 상하만 이동 가능 버전
- 장애물이 없는 버전도 따로 만들어 드릴 수 있습니다!
편하게 말씀해 주세요.
나의 말:
ChatGPT의 말:
네! 장애물이 없는 15×15 그리드에서
- (1) 경유지 없는 경우
- (2) 경유지 있는 경우
각각 BFS로 최단 경로를 찾는 코드를 만들어 드릴게요.
1. 장애물 없고 경유지 없는 경우
from collections import deque
WIDTH, HEIGHT = 15, 15
# 장애물 없음 (모두 0)
grid = [[0]*WIDTH for _ in range(HEIGHT)]
start = (13, 2)
goals = [(4, 12), (6, 12)]
directions = [(-1,0),(1,0),(0,-1),(0,1)] # 상하좌우 이동 가능
def bfs_no_waypoints_no_obstacles(start, goals, grid):
visited = set()
queue = deque()
queue.append((start[0], start[1], [start]))
visited.add((start[0], start[1]))
while queue:
x, y, path = queue.popleft()
if (x, y) in goals:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0 and (nx, ny) not in visited:
visited.add((nx, ny))
queue.append((nx, ny, path + [(nx, ny)]))
return None
# 실행 및 출력
shortest_path = bfs_no_waypoints_no_obstacles(start, goals, grid)
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")
2. 장애물 없고 경유지 있는 경우
from collections import deque
WIDTH, HEIGHT = 15, 15
# 장애물 없음 (모두 0)
grid = [[0]*WIDTH for _ in range(HEIGHT)]
start = (13, 2)
goals = [(4, 12), (6, 12)]
waypoints = [(5, 3), (8, 3), (2, 14), (3, 14)]
waypoint_indices = {pt: i for i, pt in enumerate(waypoints)}
directions = [(-1,0),(1,0),(0,-1),(0,1)]
def bfs_with_waypoints_no_obstacles(start, waypoints, goals, grid):
visited = set()
queue = deque()
start_mask = 0
if start in waypoint_indices:
start_mask = 1 << waypoint_indices[start]
queue.append((start[0], start[1], start_mask, [start]))
visited.add((start[0], start[1], start_mask))
full_mask = (1 << len(waypoints)) - 1
while queue:
x, y, mask, path = queue.popleft()
if (x, y) in goals and mask == full_mask:
return path
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < WIDTH and 0 <= ny < HEIGHT:
if grid[ny][nx] == 0:
nmask = mask
if (nx, ny) in waypoint_indices:
nmask |= 1 << waypoint_indices[(nx, ny)]
state = (nx, ny, nmask)
if state not in visited:
visited.add(state)
queue.append((nx, ny, nmask, path + [(nx, ny)]))
return None
# 실행 및 출력
shortest_path = bfs_with_waypoints_no_obstacles(start, waypoints, goals, grid)
if shortest_path:
for y in range(HEIGHT):
row = ''
for x in range(WIDTH):
if (x, y) == start:
row += 'S '
elif (x, y) in goals:
row += 'G '
elif (x, y) in waypoints:
row += 'W '
elif (x, y) in shortest_path:
row += '* '
else:
row += '. '
print(row)
else:
print("경로를 찾을 수 없습니다.")'Codyssey > AI선발팀프로젝트' 카테고리의 다른 글
| caffee_map 분석 (2) | 2025.07.24 |
|---|---|
| A* 휴리스틱 알고리즘 + JPS (0) | 2025.07.24 |
| 최단거리 그리기 matlib (4) | 2025.07.22 |
| pandas dataFrame 연습 (0) | 2025.07.19 |
| csv 파일을 불러와 내용을 출력하고 분석합니다. (0) | 2025.07.19 |