나뭇잎 교과서

여러 가지 알고리즘

같은 문제라도 알고리즘에 따라 걸리는 시간이 크게 다릅니다. 자료를 순서대로 늘어놓는 정렬과, 원하는 자료를 찾는 탐색은 컴퓨터가 가장 많이 하는 일입니다. 이 절에서는 버블 정렬과 병합 정렬의 비교 횟수를 경주로 비교하고, 업다운 게임에서 가운데를 묻는 이진 탐색이 왜 강력한지 확인합니다.

먼저 알면 좋은 것 재귀적 알고리즘로그
본문

여러 가지 알고리즘

본문

친구가 1부터 1000 사이의 수 하나를 마음속에 정했다. 내가 수를 말하면 친구는 "더 커", "더 작아", "맞아" 가운데 하나로 답한다. 1부터 차례로 물으면 최악의 경우 1000번이 걸리지만, 매번 남은 범위의 한가운데를 물으면 10번 안에 반드시 맞힐 수 있다고 한다. 또 반 학생 100명의 시험 점수를 차례로 정렬할 때, 이웃한 두 점수를 비교해 바꾸는 방법은 4950번 비교하지만 다른 방법은 600번 남짓이면 된다. 같은 일을 하는 알고리즘인데 왜 이렇게 차이가 날까?

일알고리즘최악의 비교 횟수 (n)n 수 찾기차례로 묻기n = 10001000 수 찾기이진 탐색⌈log₂(n + 1)⌉ = 101000 정렬거품 정렬n(n − 1)/2 = 4950100 정렬병합 정렬573 이하100
그림 1. 문제를 절반씩 줄이는 알고리즘은 하나씩 처리하는 알고리즘보다 비교 횟수가 훨씬 적다.
먼저 알아야 할 것재귀적 알고리즘(재귀적 알고리즘), 로그.

2.5.1절반씩 버리는 탐색

정렬된 범위에서 수를 찾을 때 한가운데를 물으면, 답이 무엇이든 남은 범위가 절반으로 준다. 1000개의 후보가 500개, 250개, 125개로 줄어들어 10번이면 1개가 남는다. \(2^{10} = 1024\)이 1000보다 크기 때문이다. 이것이 이진 탐색이다. 후보가 백만 개로 늘어도 20번, 10억 개여도 30번이면 된다. 반면 하나씩 묻는 순차 탐색은 후보 수만큼 물어야 하므로, 후보가 많을수록 두 방법의 차이는 엄청나게 벌어진다. 다만 이진 탐색은 자료가 크기 순으로 정렬되어 있어야 쓸 수 있다.

정리 2.5.1 알고리즘의 효율

크기 \(n\)인 정렬된 자료에서 이진 탐색의 최악의 비교 횟수는 \(\lceil\log_2(n + 1)\rceil\)이다. 거품 정렬의 비교 횟수는 \(\dfrac{n(n-1)}{2}\)로 \(n^2\)에 비례하고, 병합 정렬의 비교 횟수는 \(n\log_2 n\)에 비례한다.

거품 정렬은 이웃한 두 수를 비교해 큰 수를 뒤로 보내는 일을 되풀이해, 한 바퀴 돌 때마다 가장 큰 수가 맨 뒤로 간다. 모든 쌍을 비교하는 셈이어서 비교 횟수가 \(n^2\)에 비례한다. 병합 정렬은 자료를 절반으로 나누어 각각 재귀적으로 정렬한 뒤, 정렬된 두 묶음을 앞에서부터 비교하며 합친다. 나누는 단계가 \(\log_2 n\)번이고 각 단계에서 합치는 비교가 \(n\)번 정도이므로 \(n\log_2 n\)에 비례한다. 그래서 자료가 많을수록 절반씩 나누는 방법이 크게 유리하다.1

2.5.2자료가 커지면 차이도 커진다

알고리즘의 효율은 자료의 크기 \(n\)이 커질 때 계산량이 어떻게 늘어나는지로 비교한다. 자료가 100개일 때 거품 정렬과 병합 정렬의 비교 횟수는 8배쯤 차이 나지만, 100만 개이면 \(n^2/2 \approx 5\times10^{11}\)과 \(n\log_2 n \approx 2\times10^7\)으로 2만 배 넘게 차이 난다. 1초에 비교를 10억 번 하는 컴퓨터로도 거품 정렬은 8분 넘게, 병합 정렬은 0.02초쯤 걸린다. 그래서 큰 자료를 다루는 프로그램에서는 알고리즘의 선택이 컴퓨터의 속도보다 훨씬 중요하다.

n101001000100만 n(n − 1)/2454950499500약 5 × 10¹¹ n log₂ n336649966약 2 × 10⁷ log₂ n3.36.61020
그림 2. 자료가 커질수록 \(n^2\), \(n\log n\), \(\log n\)의 차이가 엄청나게 벌어진다.

2.5.3예제 풀이

예제 2.5.2

처음 질문의 수 맞히기에서 친구가 정한 수가 700이라면, 이진 탐색으로 묻는 수를 차례로 적어라. 남은 범위의 한가운데는 작은 쪽으로 버림한다. 또 1부터 1000까지에서 10번이면 반드시 맞히는 까닭을 설명하여라.

범위 1~1000에서 500을 물으면 "더 커"이므로 501~1000이 남는다. 750을 물으면 "더 작아"로 501~749, 625를 물으면 "더 커"로 626~749, 687을 물으면 "더 커"로 688~749, 718을 물으면 "더 작아"로 688~717, 702를 물으면 "더 작아"로 688~701, 694를 물으면 "더 커"로 695~701, 698을 물으면 "더 커"로 699~701이 남고, 700을 물어 "맞아"이다. 모두 9번이다.

한 번 물을 때마다 남은 후보가 절반 이하로 줄어, \(k\)번 물은 뒤 남은 후보는 \(1000/2^k\) 이하이다. \(2^{10} = 1024 > 1000\)이므로 10번이면 후보가 한 개 이하가 되어 반드시 맞힌다. 반대로 9번으로는 \(2^9 = 512\)가지밖에 구별할 수 없어 어떤 수는 맞히지 못한다. 즉 같은 일을 하는 알고리즘도 문제를 어떻게 줄여 가느냐에 따라 효율이 크게 다르며, 이진 탐색과 병합 정렬처럼 절반씩 나누는 방법은 자료가 커질수록 하나씩 처리하는 방법보다 훨씬 빠르다.

아래 탐구 카드에서 여러 정렬 방법의 비교 횟수를 직접 세어 경주시켜 보고, 1부터 1000까지의 수를 몇 번 만에 맞히는지 확인해 보자.

  1. 비교만으로 정렬하는 어떤 알고리즘도 최악의 경우 \(\log_2(n!)\)번보다 적게 비교할 수 없다는 것이 증명되어 있다. \(n!\)가지 배열을 구별하려면 예, 아니오로 답하는 질문이 그만큼 필요하기 때문이며, 이 값이 대략 \(n\log_2 n\)이어서 병합 정렬은 거의 가장 빠른 방법이다.
마무리

알고리즘의 효율은 자료의 크기 \(n\)이 커질 때 계산량이 늘어나는 빠르기로 비교하며, 이진 탐색은 \(\log_2 n\), 병합 정렬은 \(n\log_2 n\), 거품 정렬은 \(n^2\)에 비례한다. 문제를 절반씩 나누는 알고리즘은 자료가 커질수록 하나씩 처리하는 알고리즘보다 훨씬 빠르다. 처음 질문의 수 맞히기는 \(2^{10} > 1000\)이어서 이진 탐색으로 10번 안에 반드시 맞힐 수 있다.

탐구 1

정렬 방법에 따라 비교 횟수는 얼마나 달라질까?

정렬 알고리즘 · 시뮬레이션

버블 정렬

병합 정렬

  1. 질문시험지 32장을 번호순으로 정리할 때, 이웃한 두 장을 비교해 바꾸기를 반복하는 방법과 반씩 나눠 정리한 뒤 합치는 방법 중 어느 쪽이 빠를까? 장수가 1000장, 100만 장이 되면 차이는 얼마나 커질까?
  2. 복원\(1 + 2 + \cdots + (n - 1) = \frac{n(n - 1)}{2}\). \(n\)을 계속 반으로 나누면 \(\log_2 n\)번 만에 1이 된다.
  3. 설명버블 정렬은 이웃한 두 수를 비교해 큰 수를 뒤로 보내는 일을 되풀이하므로 비교가 \(\frac{n(n-1)}{2}\)번, \(n^2\)에 비례한다. 병합 정렬은 재귀적으로 반씩 나누어 정렬한 뒤 두 정렬된 목록을 앞에서부터 비교하며 합친다. 비교 횟수 \(T(n)\)은 \(T(n) \le 2T(n/2) + n\)을 만족해 약 \(n\log_2 n\)이다. \(n = 1000\)이면 약 50만 번 대 약 1만 번이다.
  4. 연결알고리즘의 효율은 입력 크기 \(n\)에 대한 연산 횟수의 증가 빠르기로 비교한다. 빠른 거듭제곱 카드의 곱셈 횟수(\(n\) 대 \(\log n\)), 다음 카드의 이진 탐색도 같은 관점이다.
  5. 적용경주를 시작하고 두 방법의 비교 횟수를 보자. 자료 수를 두 배로 하면 버블 정렬의 비교 횟수는 몇 배가 되는가? 병합 정렬은요? 아래 그래프에서 두 곡선이 벌어지는 모습을 확인해 보자.
  6. 판단자료가 아주 적거나 이미 거의 정렬되어 있으면 단순한 방법이 더 빠를 수 있다. 실제 프로그램은 상황에 따라 여러 정렬을 섞어 쓴다. 비교 횟수만이 아니라 필요한 메모리(병합 정렬은 추가 공간이 필요)도 따져야 한다.

확인조건이 달라지면

버블 정렬로 \(n\)개를 정렬하는 데 1초가 걸렸다면, \(10n\)개는 대략 얼마나 걸릴까?

탐구 2

1부터 1000까지의 수를 몇 번 만에 맞힐 수 있을까?

이진 탐색 · 예측→확인

먼저 예측해 보자. 가장 운이 나빠도 몇 번이면 반드시 맞힐까?

  1. 질문친구가 1부터 1000 사이의 수를 하나 정했다. 수를 말하면 "업"(더 큼) 또는 "다운"(더 작음)을 알려 준다. 가장 운이 나쁜 경우에도 반드시 맞히려면 최소 몇 번 물어야 할까?
  2. 복원\(2^{10} = 1024\). 반씩 나누기를 \(k\)번 하면 후보가 \(\frac{1}{2^k}\)로 줄어든다.
  3. 설명남은 후보의 가운데 수를 묻으면 어떤 답이 와도 후보가 절반 이하로 줄어든다. 이것이 이진 탐색이다. 후보 \(N\)개는 \(k\)번 만에 \(N/2^k\)개 이하가 되므로 \(2^k > N\), 곧 \(k = \lceil\log_2(N + 1)\rceil\)번이면 반드시 맞힌다. 1000개면 10번, 100만 개라도 20번이다. 반대로 하나씩 차례로 묻는 순차 탐색은 최악에 \(N\)번이 걸린다.
  4. 연결이진 탐색은 자료가 정렬되어 있을 때만 쓸 수 있다. 그래서 앞 카드의 정렬이 먼저 필요하다. 사전에서 단어를 찾을 때, 데이터베이스가 기록을 찾을 때 같은 원리를 쓴다.
  5. 적용먼저 최악의 횟수를 예측하고 게임을 해 보자. 내가 쓴 횟수와 "이진 탐색으로 풀기"의 횟수를 비교해 보자. 범위를 100만으로 바꾸면 몇 번이 필요한가?
  6. 판단운이 좋으면 한 번에 맞힐 수도 있지만, 알고리즘의 성능은 보통 "가장 나쁜 경우"로 보장한다. 또 가운데가 아닌 곳을 물으면 나쁜 답이 왔을 때 후보가 절반보다 많이 남아 최악의 횟수가 늘어난다.

확인조건이 달라지면

정렬된 자료 5000개에서 이진 탐색으로 찾을 때 최악의 비교 횟수는?

AI 출제 문항연습문제 풀어보기10문항 수능·모의평가·학력평가기출문제 풀어보기5문항
Esc

↑↓ 고르기Enter 열기