나뭇잎 교과서

상태 공간 완전 탐색

가능한 경우를 모두 따져 보면 답이 틀릴 수 없습니다. 문제는 "모든 경우"를 빠짐없이, 겹치지 않게 늘어놓는 방법입니다. 문제를 상태와 결정으로 나타내면 모든 경우가 하나의 트리가 되고, 컴퓨터는 그 트리를 차례로 훑습니다.

본문

상태 공간 완전 탐색

본문

물건 20개 가운데 몇 개를 골라 가방에 넣어 가치를 가장 크게 하려 한다. 가능한 모든 조합을 따져 보면 답이 틀릴 리 없다. 20개의 조합은 약 100만 가지라 컴퓨터는 1초도 안 되어 끝낸다. 그런데 물건이 40개이면 1조 가지가 넘어 몇십 분이 걸리고, 60개이면 수십 년이 걸린다. 순서까지 정해야 하는 문제는 더 빨리 감당할 수 없게 된다. 완전 탐색은 언제 쓸 수 있고, 모든 경우를 빠짐없이 늘어놓으려면 어떻게 해야 할까?

그림 1. 대상 n개를 고를지 말지 정하는 경우의 수 2ⁿ(파랑)과 순서를 정하는 경우의 수 n!(주황), 로그 눈금. 회색 선은 1초에 검사할 수 있는 10억 가지이다.
먼저 알아야 할 것경우의 수와 순열, 조합(수학), 재귀 구조(재귀 구조), 트리(트리와 그래프의 구현).

3.1.1상태와 결정으로 나타내기

완전 탐색은 가능한 모든 경우를 하나도 빠짐없이, 겹치지 않게 살펴보는 방법이다. 그러려면 문제를 상태와 결정으로 나타낸다. 물건 고르기라면 '지금까지 어떤 물건을 골랐는가'가 상태이고, '다음 물건을 넣을까 말까'가 결정이다. 처음 상태에서 결정을 하나씩 내릴 때마다 상태가 갈라지므로, 모든 경우는 하나의 트리가 된다. 이 트리를 상태 공간 트리라 한다. 컴퓨터는 재귀 함수로 트리를 깊이 우선으로 훑으며, 잎에 이를 때마다 그 경우가 답의 조건을 만족하는지 확인한다.

정리 3.1.1 상태 공간의 크기

\(n\)개의 대상마다 고를지 말지 두 가지로 결정하면 경우의 수는 \(2^n\)이고, \(n\)개의 순서를 모두 정하면 \(n!\)이다. 완전 탐색의 시간은 경우의 수에 비례하므로, \(n\)이 조금만 커져도 지수적으로 늘어난다.

그림 1에서 \(n\)을 바꾸어 보자. 1초에 10억 가지를 검사한다고 하면, 고르기 문제는 \(n = 30\)에서 1초, \(n = 40\)에서 약 18분, \(n = 50\)에서 약 13일이 걸린다. 순서 문제는 더 빨라서 \(n = 13\)에서 6초, \(n = 15\)에서 약 22분, \(n = 20\)에서 약 77년이다. 그래서 완전 탐색은 대상이 수십 개를 넘지 않는 문제에 쓰고, 그보다 크면 쓸모없는 가지를 잘라 내거나 다른 방법을 찾는다.1

3.1.2빠짐없이, 겹치지 않게

완전 탐색이 맞는 답을 주려면 모든 경우를 한 번씩만 살펴야 한다. 같은 경우를 두 번 살피면 시간이 낭비되고, 하나라도 빠뜨리면 그것이 답일 때 틀린다. 결정을 정해진 순서로 하나씩 내리는 상태 공간 트리는 이 두 조건을 저절로 만족한다. 순열을 만들 때는 아직 쓰지 않은 원소만 다음 자리에 놓고, 조합을 만들 때는 앞에서 고른 원소보다 뒤의 원소만 고르면 같은 조합을 두 번 만들지 않는다. 그리고 탐색하기 전에 경우의 수를 먼저 계산해 보면, 그 문제를 완전 탐색으로 풀 수 있는지 미리 판단할 수 있다.

문제결정경우의 수 물건 고르기넣는다, 안 넣는다2ⁿ 방문 순서 정하기다음에 갈 곳n! r개 고르기뒤의 원소 가운데 하나ₙCᵣ
그림 2. 결정의 모양이 상태 공간의 크기를 정한다.

3.1.3예제 풀이

예제 3.1.2

무게 한도 10 kg인 가방에 물건 네 개(무게 5, 4, 6, 3 kg, 가치 10, 40, 30, 50)를 넣는다. 가능한 경우의 수를 구하고, 완전 탐색으로 가치가 가장 큰 조합을 찾아라.

물건마다 넣을지 말지 두 가지이므로 \(2^4 = 16\)가지이다. 무게가 10 kg 이하인 조합 가운데 가치가 큰 것을 찾는다. 4 kg과 3 kg을 넣으면 7 kg, 가치 90이다. 여기에 5 kg을 더하면 12 kg으로 넘고, 6 kg을 더해도 13 kg으로 넘는다.

6 kg과 4 kg은 10 kg, 가치 70이고, 6 kg과 3 kg은 9 kg, 가치 80이다. 5 kg과 4 kg은 가치 50, 5 kg과 3 kg은 가치 60이다. 16가지를 모두 살펴보면 4 kg과 3 kg의 조합이 가치 90으로 가장 크다. 물건이 4개이면 손으로도 다 따질 수 있지만, 40개이면 1조 가지가 넘는다. 즉 완전 탐색은 언제나 맞는 답을 주지만, 그 값은 경우의 수만큼의 시간이다. 그래서 다음 절에서는 살펴볼 필요가 없는 가지를 미리 잘라 내는 방법을 배운다.

아래 탐구 카드에서 문제를 상태와 결정으로 나타내 상태 공간 트리를 그리고, 그 트리를 재귀로 훑어 보자.

  1. 체스에서 둘 수 있는 게임의 가짓수는 약 \(10^{120}\)으로 추정되어, 관측 가능한 우주의 원자 수(약 \(10^{80}\))보다 훨씬 많다. 그래서 체스 프로그램은 완전 탐색 대신 가지치기와 평가 함수를 쓴다.
마무리

완전 탐색은 문제를 상태와 결정으로 나타내 모든 경우를 상태 공간 트리로 만들고, 그 트리를 빠짐없이 겹치지 않게 훑어 언제나 맞는 답을 찾는다. 그런데 고르기 문제는 \(2^n\), 순서 문제는 \(n!\)가지로 경우의 수가 지수적으로 늘어, 대상이 수십 개를 넘으면 감당할 수 없다. 그래서 처음 질문의 완전 탐색은 고르기 문제에서 30~40개, 순서 문제에서 13~15개 정도까지만 현실적인 시간 안에 통한다.

탐구 1

배낭에 넣을 물건을 고르는 경우는 모두 몇 가지일까?

상태 공간 · 완전 탐색 · 0/1 배낭 문제
방문한 상태 (노드)
—
확인한 완성 상태 (잎)
—
지금까지 최고 가치
—


              

물건의 무게와 가치는 설명을 위해 정한 예시이다. 코드는 처음에 best = −1, n·cap·W·V가 정해져 있다고 보고 search(0, 0, 0)으로 시작한다.

  1. 질문무게 제한이 10 kg인 배낭에 물건 네 개 중 몇 개를 골라 넣어, 가치의 합을 가장 크게 하려 한다. 눈으로 몇 가지 조합을 해 보면 그럴듯한 답은 나온다. 그 답이 정말 가장 좋은지 어떻게 확신할 수 있을까?
  2. 복원재귀 함수는 같은 모양의 더 작은 문제를 스스로 호출하고, 더 쪼갤 수 없는 기저 사례에서 멈춘다. 트리는 뿌리에서 갈라져 내려가는 구조이고, 깊이 우선으로 훑을 수 있다.
  3. 설명문제를 상태로 나타내면 탐색할 수 있다. 여기서 상태는 "i번째 물건까지 정했고, 지금까지 무게 w, 가치 v"이다. 처음 상태는 (0, 0, 0)이고, 한 번의 결정(넣는다/안 넣는다)이 상태를 다음 상태로 바꾼다. 가능한 상태를 모두 모은 것이 상태 공간이고, 결정을 가지로 그리면 상태 공간 트리가 된다. 완전 탐색은 이 트리의 모든 잎을 확인해 가장 좋은 것을 고른다. 모든 경우를 보므로 답이 틀릴 수 없다.
  4. 연결물건마다 선택지가 2개이므로 잎은 2ⁿ개, 노드는 2ⁿ⁺¹ − 1개이다. n = 20이면 잎이 1,048,576개, n = 30이면 약 10억 개, n = 40이면 약 1조 개이다. 순서를 정하는 문제(예를 들어 도시 n곳을 도는 순서)는 상태 수가 n!로, 2ⁿ보다도 빨리 커진다(10! = 3,628,800).
  5. 적용n = 4, cap = 10에서 끝까지 실행해 최고 가치와 그 조합을 확인해 보자. 잎 몇 개가 무게를 넘었는가? cap을 바꿔도 방문하는 노드 수는 달라지는가? n을 하나 늘릴 때마다 노드 수는 몇 배가 되는가?
  6. 판단완전 탐색은 "느리고 쓸모없는 방법"이 아니다. 경우가 적으면 가장 확실하고 빨리 짤 수 있는 방법이고, 더 똑똑한 알고리즘의 답이 맞는지 검사하는 기준으로도 쓴다. 다만 상태 수가 지수적으로 늘기 때문에 n이 조금만 커져도 끝나지 않는다. 이 카드의 탐색은 무게가 이미 넘은 상태에서도 계속 내려간다. 이런 쓸데없는 가지를 잘라 내는 방법은 가지치기에서 다룬다.

확인조건이 달라지면

물건마다 "안 넣음, 1개 넣음, 2개 넣음" 세 가지를 고를 수 있게 바꾸었다. 물건이 5개일 때 완전 탐색이 확인할 완성 상태(잎)의 수는?

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

↑↓ 고르기Enter 열기