본 시리즈는 프로그래밍 알고리즘의 개념을 정리하고 실습을 진행해보는 시리즈입니다.
탐색(Search) 알고리즘은 주어진 데이터 구조 내에서 원하는 데이터를 찾아내기 위한 일련의 과정입니다.
탐색 알고리즘은 크게 두 가지 범주로 나눌 수 있습니다:
이번 시리즈에서는 이러한 다양한 탐색 알고리즘을 순차적으로 다루면서 그 개념과 활용 방법을 소개합니다.
이번 포스팅에서는 백트래킹(Backtracking)을 중점적으로 다루도록 하겠습니다.
백트래킹(Backtracking)은 해를 찾기 위해 모든 가능한 경우의 수를 탐색하는 기법 중 하나로, 조건에 맞지 않는 해를 도중에 포기하고 다른 경로를 탐색하는 방식입니다.
백트래킹은 다음과 같은 특징을 가지고 있습니다:
탐색 과정 중 실패 가능성: 특정 경로가 문제의 해답이 될 수 없음을 알게 되면, 그 경로의 탐색을 즉시 중단하고 다른 경로를 탐색합니다. 이 과정을 백트래킹이라고 부르며, 이는 마치 길을 잘못 들었을 때 다시 돌아와 새로운 길을 찾는 것과 유사합니다.
재귀적 탐색: 백트래킹은 주로 재귀적으로 구현되며, 문제를 작은 부분으로 나누어 하위 문제를 해결하고, 조건에 맞지 않으면 다시 돌아와 다른 하위 문제를 탐색합니다.
최적화 및 탐색 문제: 백트래킹은 최적화 문제와 탐색 문제에 적합합니다. 특히, 해의 구조가 나뉘는 문제에 매우 적합하며, N-Queen 문제, 미로 탐색, 순열과 조합 등에서 자주 사용됩니다.
백트래킹의 탐색 방식은 깊이 우선 탐색(DFS)과 유사하지만, 가능성이 없는 경로는 중간에 포기한다는 점에서 차이가 있습니다.
백트래킹은 이처럼 경로가 올바르지 않다면 되돌아가서 다른 경로를 찾는 것을 기본으로 하며, 탐색 효율성을 극대화하는 데 주로 사용됩니다.
Backtracking 알고리즘은 모든 가능한 해를 탐색하되, 유망하지 않은 경로를 빠르게 제외함으로써 탐색 범위를 줄이는 방식입니다. 이 방식은 순열, 조합, 또는 최적화 문제와 같은 경우에 유용하게 사용됩니다.
N-Queen 문제는 대표적인 Backtracking 알고리즘의 예시로, N x N 체스판 위에 N개의 퀸(Queen)을 서로 공격할 수 없도록 배치하는 문제입니다.
Python 코드 구현:
def is_safe(board, row, col, n):
# 같은 열에 퀸이 있는지 확인
for i in range(row):
if board[i][col] == 1:
return False
# 왼쪽 대각선 위에 퀸이 있는지 확인
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False
# 오른쪽 대각선 위에 퀸이 있는지 확인
for i, j in zip(range(row, -1, -1), range(col, n)):
if board[i][j] == 1:
return False
return True
def solve_n_queens(board, row, n):
if row >= n:
# 퀸이 모두 배치된 경우, 결과 출력
for line in board:
print(line)
print()
return True
result = False
for col in range(n):
if is_safe(board, row, col, n):
board[row][col] = 1 # 퀸 배치
result = solve_n_queens(board, row + 1, n) or result
board[row][col] = 0 # Backtracking
return result
# N-Queen 문제 실행 (N=4)
n = 4
board = [[0 for _ in range(n)] for _ in range(n)]
solve_n_queens(board, 0, n)
[0, 1, 0, 0]
[0, 0, 0, 1]
[1, 0, 0, 0]
[0, 0, 1, 0]
[0, 0, 1, 0]
[1, 0, 0, 0]
[0, 0, 0, 1]
[0, 1, 0, 0]
위 코드에서는 재귀를 사용해 각 행에 퀸을 배치한 후, 유효하지 않으면 다시 퀸을 제거하고 다른 경로를 탐색하는 방식입니다.
Java 코드 구현:
public class NQueens {
private static boolean isSafe(int[][] board, int row, int col, int n) {
// 같은 열에 퀸이 있는지 확인
for (int i = 0; i < row; i++) {
if (board[i][col] == 1) {
return false;
}
}
// 왼쪽 대각선 위에 퀸이 있는지 확인
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 1) {
return false;
}
}
// 오른쪽 대각선 위에 퀸이 있는지 확인
for (int i = row, j = col; i >= 0 && j < n; i--, j++) {
if (board[i][j] == 1) {
return false;
}
}
return true;
}
private static boolean solveNQueens(int[][] board, int row, int n) {
if (row >= n) {
printSolution(board, n);
return true;
}
boolean result = false;
for (int col = 0; col < n; col++) {
if (isSafe(board, row, col, n)) {
board[row][col] = 1; // 퀸 배치
result = solveNQueens(board, row + 1, n) || result;
board[row][col] = 0; // Backtracking
}
}
return result;
}
private static void printSolution(int[][] board, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) {
int n = 4; // N-Queen 문제 (N=4)
int[][] board = new int[n][n];
solveNQueens(board, 0, n);
}
}
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0
Backtracking은 최단 경로 문제에서도 사용할 수 있습니다.
Python 코드 구현:
def is_safe(maze, x, y, solution):
# 좌표가 미로 범위 안에 있는지 먼저 확인
if not (0 <= x < len(maze) and 0 <= y < len(maze)):
return False
# 그 후에 통로인지 확인 (1이면 통로, 0이면 벽)
if maze[x][y] != 1:
return False
# 마지막으로 해당 좌표가 아직 방문되지 않았는지 확인
if solution[x][y] != 0:
return False
return True
def solve_maze(maze):
n = len(maze)
solution = [[0 for _ in range(n)] for _ in range(n)]
if solve_maze_util(maze, 0, 0, solution):
for line in solution:
print(line)
else:
print("No solution exists")
def solve_maze_util(maze, x, y, solution):
if x == len(maze) - 1 and y == len(maze) - 1:
solution[x][y] = 1
return True
if is_safe(maze, x, y, solution):
solution[x][y] = 1
if solve_maze_util(maze, x + 1, y, solution):
return True
if solve_maze_util(maze, x, y + 1, solution):
return True
solution[x][y] = 0 # Backtracking
return False
return False
# 테스트용 미로 (1: 통로, 0: 벽)
maze = [
[1, 0, 0, 0],
[1, 1, 0, 1],
[0, 1, 0, 0],
[1, 1, 1, 1]
]
solve_maze(maze)
[1, 0, 0, 0]
[1, 1, 0, 0]
[0, 1, 0, 0]
[0, 1, 1, 1]
Java 코드 구현:
public class MazeSolver {
private static boolean isSafe(int[][] maze, int x, int y, int[][] solution) {
return x >= 0 &&
x < maze.length &&
y >= 0 && y < maze[0].length &&
maze[x][y] == 1 && solution[x][y] == 0;
}
public static void solveMaze(int[][] maze) {
int n = maze.length;
int[][] solution = new int[n][n];
if (solveMazeUtil(maze, 0, 0, solution)) {
printSolution(solution);
} else {
System.out.println("No solution exists");
}
}
private static boolean solveMazeUtil(int[][] maze, int x, int y, int[][] solution) {
if (x == maze.length - 1 && y == maze.length - 1) {
solution[x][y] = 1;
return true;
}
if (isSafe(maze, x, y, solution)) {
solution[x][y] = 1;
if (solveMazeUtil(maze, x + 1, y, solution)) {
return true;
}
if (solveMazeUtil(maze, x, y + 1, solution)) {
return true;
}
solution[x][y] = 0; // Backtracking
return false;
}
return false;
}
private static void printSolution(int[][] solution) {
for (int[] row : solution) {
for (int cell : row) {
System.out.print(cell + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] maze = {
{1, 0, 0, 0},
{1, 1, 0, 1},
{0, 1, 0, 0},
{1, 1, 1, 1}
};
solveMaze(maze);
}
}
1 0 0 0
1 1 0 0
0 1 0 0
0 1 1 1
백트래킹(Backtracking) 알고리즘은 재귀적으로 모든 가능한 해를 탐색하되, 유망하지 않은 경로는 중간에 탐색을 중단하여 효율성을 높입니다.
백트래킹의 시간 복잡도는 문제의 종류에 따라 달라지지만, 일반적으로 탐색해야 할 경로의 수에 비례합니다.
N-Queen 문제의 경우:
미로 탐색 문제의 경우:
백트래킹의 공간 복잡도는 일반적으로 재귀 깊이에 따라 달라집니다.
N-Queen 문제의 경우:
미로 탐색 문제의 경우:
백트래킹은 가지치기(Pruning)를 통해 불필요한 경로를 미리 포기함으로써 시간 복잡도를 줄일 수 있습니다. 이를 통해 탐색 범위를 줄일 수 있지만, 문제의 특성에 따라 최악의 경우에는 여전히 모든 경로를 탐색해야 합니다.
이번 포스팅에서는 백트래킹(Backtracking) 알고리즘에 대해 살펴보았습니다.
백트래킹은 모든 가능한 해를 탐색하는 방식이지만, 조건에 맞지 않는 경로는 중간에 탐색을 중단하여 효율성을 높이는 알고리즘입니다.
N-Queen 문제와 미로 탐색 문제를 통해 백트래킹의 원리와 구현 방법을 살펴보았습니다.
백트래킹의 시간 복잡도는 문제의 특성에 따라 달라지며, 최악의 경우 모든 경로를 탐색해야 하지만, 가지치기(Pruning) 기법을 통해 일부 경로를 미리 포기할 수 있습니다.
다음 포스팅에서는 최적화 기법 중 하나인 Greedy(탐욕 알고리즘)을 다룰 예정입니다.