나뭇잎 교과서

가지치기 탐색

완전 탐색은 확실하지만 경우의 수가 지수적으로 늘어 금방 감당할 수 없게 됩니다. 이미 실패가 확실한 가지, 더 좋은 답이 나올 수 없는 가지를 미리 알아보고 버리면 같은 답을 훨씬 적은 일로 찾을 수 있습니다. 무엇을 근거로 버려야 답이 틀리지 않을까요?

본문

가지치기 탐색

본문

체스의 퀸은 가로, 세로, 대각선으로 몇 칸이든 움직여 공격한다. 8 × 8 판에 퀸 8개를 서로 공격하지 않게 놓는 방법을 찾으려 한다. 행마다 퀸을 하나씩 놓는다고 해도, 행마다 8칸 가운데 하나를 고르면 \(8^8\), 곧 약 1677만 가지이다. 이것을 모두 놓아 보고 검사하는 것은 낭비처럼 보인다. 첫 두 행의 퀸이 이미 서로 공격한다면, 나머지 여섯 행을 어떻게 놓든 답이 될 수 없기 때문이다. 이런 경우를 어떻게 미리 알아보고 버릴 수 있을까?

그림 1. 행마다 퀸을 아무 칸에나 놓는 완전 탐색의 경우의 수(nⁿ)와, 이미 공격받는 칸에서 바로 되돌아오는 백트래킹이 실제로 방문하는 상태의 수(막대는 로그 길이). 상태 수는 이 그림의 프로그램이 직접 세었다.
먼저 알아야 할 것상태 공간 완전 탐색(상태 공간 완전 탐색), 재귀와 스택(재귀 구조).

3.2.1백트래킹

백트래킹은 상태 공간 트리를 깊이 우선으로 훑되, 지금까지의 결정만으로 이미 조건을 어긴 상태에 이르면 그 아래 가지를 더 살피지 않고 바로 되돌아오는 탐색이다. N-퀸 문제에서는 위의 행부터 퀸을 하나씩 놓으며, 새 퀸이 앞의 퀸들과 같은 열이나 같은 대각선에 있으면 그 칸을 버린다. 그 행의 어느 칸에도 놓을 수 없으면 한 행 위로 되돌아가 그 퀸을 다음 칸으로 옮긴다. 이렇게 실패가 확실한 가지를 일찍 잘라 내는 것을 가지치기라 한다.

정리 3.2.1 가지치기가 답을 지키는 조건

가지치기로 버리는 가지는 그 아래 어디에도 답이 없다는 것이 확실해야 한다. 조건을 이미 어긴 부분 해는 결정을 더해도 조건을 되살릴 수 없으므로 버려도 된다. 최적화 문제에서는 그 가지에서 얻을 수 있는 가장 좋은 값이 지금까지 찾은 답보다 나쁠 때 버린다(분기 한정). 확실하지 않은 가지를 버리면 답을 놓칠 수 있다.

그림 1에서 판의 크기를 바꾸어 보자. 8 × 8에서 완전 탐색은 약 1677만 가지이지만, 백트래킹은 2천 개 남짓의 상태만 방문하고 92개의 해를 모두 찾는다. 8천 배 넘게 일이 준다. 퀸 하나를 놓을 때마다 그 열과 두 대각선이 막혀, 다음 행에서 놓을 수 있는 칸이 빠르게 줄기 때문이다. 판이 커질수록 그 차이는 더 크게 벌어진다.1

3.2.2더 좋은 답이 나올 수 없는 가지

가방 문제처럼 가장 좋은 답을 찾는 문제에서는 조건을 어긴 가지뿐 아니라, 더 좋은 답이 나올 수 없는 가지도 버린다. 지금까지 찾은 가장 큰 가치가 90인데, 어떤 가지에서 남은 물건을 무게 제한 없이 모두 넣어도 가치가 80을 넘지 못한다면 그 가지는 볼 필요가 없다. 이때 '아무리 잘해도 얻을 수 있는 값'을 쉽게, 그러면서도 너무 낮지 않게 어림하는 것이 중요하다. 어림이 정확할수록 많은 가지를 자를 수 있지만, 어림이 틀려 실제보다 낮게 잡으면 답을 놓친다.

버리는 가지근거예 조건을 이미 어긴 가지결정을 더해도 되살릴 수 없음N-퀸, 스도쿠 더 좋은 답이 없는 가지얻을 수 있는 최댓값 ≤ 지금 답가방 문제
그림 2. 가지치기는 그 가지에 답이 없다는 것이 확실할 때만 한다.

3.2.3예제 풀이

예제 3.2.2

4 × 4 판에 퀸 4개를 놓는다. 첫 행의 퀸을 첫째 칸에 놓았을 때, 백트래킹으로 둘째 행부터 차례로 놓아 보며 해가 있는지 찾아라. 해가 없으면 첫 행의 퀸을 둘째 칸으로 옮겨 다시 찾아라. 열은 왼쪽부터 1, 2, 3, 4이다.

첫 행 1열에 놓으면 둘째 행의 1열(같은 열)과 2열(대각선)이 막혀 3열에 놓는다. 셋째 행은 1, 3열(열), 2, 4열(대각선)이 모두 막혀 되돌아간다. 둘째 행을 4열로 옮기면 셋째 행은 2열만 가능하고, 넷째 행은 모든 칸이 막힌다. 그래서 첫 행 1열에서는 해가 없다.

첫 행을 2열로 옮기면 둘째 행은 4열, 셋째 행은 1열, 넷째 행은 3열에 놓을 수 있어 해 (2, 4, 1, 3)을 찾는다. 완전 탐색이라면 \(4^4 = 256\)가지를 모두 검사해야 했지만, 백트래킹은 막힌 칸에서 곧바로 돌아와 수십 개의 상태만 보았다. 즉 가지치기는 같은 답을 훨씬 적은 일로 찾게 해 준다. 그래서 스도쿠 풀이, 시간표 짜기, 퍼즐 같은 문제는 백트래킹으로 푼다.

아래 탐구 카드에서 N-퀸과 가방 문제를 백트래킹으로 풀고, 가지치기가 줄이는 일의 양을 재어 보자.

  1. 8-퀸 문제는 1848년 체스 문제 작가 베첼이 처음 냈고, 수학자 가우스도 관심을 가졌다. 해는 92개이며, 판을 돌리거나 뒤집어 같아지는 것을 하나로 치면 12개이다.
마무리

백트래킹은 상태 공간 트리를 깊이 우선으로 훑으며, 이미 조건을 어긴 상태나 더 좋은 답이 나올 수 없는 상태에 이르면 그 아래 가지를 버리고 되돌아온다. 버리는 가지에 답이 없다는 것이 확실하면 같은 답을 훨씬 적은 일로 찾는다. 그래서 처음 질문의 8-퀸 문제는 이미 공격받는 칸에서 곧바로 되돌아오는 가지치기로, 1677만 가지 대신 2천 개 남짓의 상태만 보고 모든 해를 찾는다.

탐구 1

가망 없는 경우를 끝까지 확인해야 할까?

가지치기 · 백트래킹 · N-Queen
속도

방문한 상태 / 전체
—
찾은 해 / 전체
—
safe 검사 횟수
0

              

초록 테두리는 안전한 칸에 퀸을 놓고 다음 행으로 내려가는 것, 주황 ×는 이미 놓인 퀸과 같은 열이나 대각선이라 그 아래 가지 전체를 버리는 것이다. 엷은 주황 칸은 공격받는 칸이다. 상태는 퀸을 몇 행까지 놓은 배치 하나이며, 빈 판(뿌리)도 하나로 센다.

  1. 질문n×n 체스판에 퀸 n개를 서로 공격하지 못하게 놓으려 한다(퀸은 가로, 세로, 대각선으로 공격한다). 8×8 판이라면 퀸을 행마다 아무 열에나 놓는 경우만 8⁸ = 16,777,216가지이다. 이것을 모두 놓아 본 뒤에 검사해야 할까?
  2. 복원완전 탐색은 상태 공간 트리의 모든 잎을 확인한다. 행 하나에 퀸 하나를 놓는다고 정하면, 트리의 깊이는 n이고 각 노드에서 가지는 n개이다.
  3. 설명퀸 두 개를 놓았는데 벌써 서로 공격한다면, 그 아래에 남은 퀸을 어떻게 놓아도 해가 될 수 없다. 그래서 그 상태 아래의 가지 전체를 보지 않고 버린다. 이것이 가지치기이고, 한 단계씩 놓다가 막히면 바로 앞 결정으로 돌아가 다른 선택을 해 보는 방식을 백트래킹이라 한다. 코드에서 safe가 False면 재귀 호출을 하지 않고, 재귀에서 돌아오면 cols.pop()으로 마지막 퀸을 치운다.
  4. 연결가지치기는 답을 바꾸지 않는다. 버린 가지에는 해가 없다는 것이 확실할 때만 자르기 때문이다. 그래서 완전 탐색과 같은 해를 모두 찾으면서도 방문하는 상태는 훨씬 적다. 8×8에서 가지치기 없이 놓으면 노드가 약 1,900만 개이지만, 백트래킹은 2,057개만 방문하고 해 92개를 모두 찾는다.
  5. 적용n = 4로 놓고 한 칸씩 시도하며 처음 막히는 곳에서 어느 행으로 되돌아가는지 따라가 보자. n = 4의 해는 몇 개인가? n을 하나씩 늘리며 세 막대의 차이가 어떻게 벌어지는지 보자. "해를 찾으면 멈춤"을 끄고 아주 빠르게 실행하면 n = 8의 해 92개를 모두 찾는 데 방문하는 상태 수를 확인할 수 있다.
  6. 판단가지치기를 해도 최악의 경우 작업량은 여전히 지수적으로 늘 수 있다. 가지치기는 "얼마나 많이 줄어드는가"가 문제마다 다르며, 확인 조건(safe)을 검사하는 비용도 든다. 또 잘못된 조건으로 자르면 해가 있는 가지까지 버려 답이 틀린다. 자르는 근거가 "이 아래에는 절대 해가 없다"는 증명이어야 한다.

확인조건이 달라지면

스도쿠를 푸는 프로그램이 빈칸을 차례로 채운다. 빈칸에 숫자를 넣을 때마다 같은 행·열·3×3 칸에 같은 숫자가 있는지 검사하고, 있으면 그 숫자를 버린다. 이 검사를 빼고 모든 칸을 다 채운 뒤에만 검사하면 어떻게 될까?

탐구 2

정답을 놓치지 않으면서 얼마나 덜 볼 수 있을까?

상태 공간 배제 · 분기 한정법 · 탐욕법

방문무게 초과로 멈춤상한으로 멈춤끝까지 결정찾은 답

방법방문 노드찾은 가치정답?


              

물건은 가치/무게가 큰 순서로 정렬해 두었다(값은 설명용 예시). bound(i, room)은 i번째부터 남은 물건을 가치/무게 순으로 넣되, 마지막 하나는 잘라서라도 넣는다고 할 때의 가치 합으로, 이 가지에서 얻을 수 있는 가치의 상한이다. 완전 탐색은 가지치기 두 줄을 빼고(무게가 cap 이하일 때만 best를 고치며) 실행한 것, 무게 초과 가지치기는 가지치기 1만 쓴 것이다.

  1. 질문물건 6개의 상태 공간 트리에는 노드가 127개 있다. 물건이 40개면 노드가 2조 개를 넘는다. 확실히 답이 될 수 없는 가지를 미리 알아보고 건너뛸 수 있다면, 정답을 보장하면서도 훨씬 적게 볼 수 있지 않을까?
  2. 복원완전 탐색은 상태 공간의 모든 잎을 확인한다. 백트래킹은 조건을 어긴 상태에서 더 내려가지 않고 되돌아간다. 여기서는 무게가 cap을 넘은 상태가 그런 경우이다.
  3. 설명분기 한정법(branch and bound)은 한 걸음 더 나아간다. 어떤 상태에서 "이 아래로 아무리 잘 골라도 얻을 수 있는 가치는 최대 얼마"라는 상한을 계산하고, 그 상한이 지금까지 찾은 최고 가치보다 크지 않으면 그 가지를 버린다. 상한은 물건을 잘라 넣을 수 있다고 가정해 쉽게 구한다. 실제 가능한 어떤 선택보다도 작지 않으므로, 이 기준으로 버린 가지에는 더 좋은 답이 없다. 탐욕법(greedy)은 아예 되돌아가지 않는다. 매 순간 가장 좋아 보이는 것(가치/무게가 가장 큰 물건)을 고르고 다시는 바꾸지 않아, 경로 하나만 본다.
  4. 연결완전 탐색, 무게 초과 가지치기, 분기 한정은 모두 같은 최적의 답을 낸다. 상태 공간의 일부를 버리지만, 버린 부분에 더 좋은 답이 없다는 근거가 있기 때문이다. 탐욕법은 가장 빠르지만 그런 근거가 없어 0/1 배낭 문제에서는 틀릴 수 있다. 반면 물건을 잘라 넣을 수 있는 배낭 문제, 거스름돈을 500·100·50·10원으로 줄 때처럼 "매 순간의 최선이 전체의 최선"임이 증명된 문제에서는 탐욕법이 정확한 답을 준다.
  5. 적용cap = 12에서 네 방법을 차례로 골라 끝까지 실행하고 방문 노드 수를 비교해 보자. 탐욕법은 어떤 답을 냈고, 정답은 무엇인가? cap을 바꿔 가며 탐욕법이 맞는 경우와 틀리는 경우를 각각 찾아보자. 분기 한정에서 주황 노드가 생기는 순간, 상한과 지금까지 최고 가치를 비교해 보자.
  6. 판단"가지치기를 하면 답이 틀릴 수 있다"와 "탐욕법도 가지치기의 한 종류라 항상 맞다"는 둘 다 잘못이다. 상태를 배제하는 근거가 증명되어 있으면 답은 그대로이고, 근거 없이 버리면(탐욕법을 아무 문제에나 쓰면) 틀린다. 또 분기 한정이 얼마나 줄여 주는지는 상한이 얼마나 정확한지와 좋은 답을 얼마나 일찍 찾는지에 달려 있어, 최악의 경우에는 여전히 지수 시간이 걸릴 수 있다.

확인조건이 달라지면

동전이 1원, 400원, 500원 세 종류뿐인 나라에서 800원을 가장 적은 동전으로 거슬러 주려고, "남은 금액보다 크지 않은 가장 큰 동전부터" 고르는 탐욕법을 썼다. 결과는?

AI 출제 문항연습문제 풀어보기12문항
Esc

↑↓ 고르기Enter 열기