알고리즘 · 정렬 경주

왜 어떤 정렬 알고리즘도 n log n을 이길 수 없을까

규칙

여러 알고리즘이 똑같이 섞인 데이터를 정렬해요. 저마다 자기 방식으로 혼돈을 질서로 바꿔요.

클릭하면 YouTube에서 재생돼요.

8 Sorting Algorithms Visualized 30 Min Ambient Focus Loop

왜 이렇게 될까

카드 한 벌을 섞고, 두 장씩 비교해서 정렬해 보세요. 어떤 방법은 이웃한 카드를 몇 번이고 비교하고, 어떤 방법은 카드를 반으로, 또 반의 반으로 나눠요. 똑같이 섞인 카드에서 두 번째 방법이 훨씬 빨리 끝나요. 비교만 하는 방법으로는 그보다 크게 나아질 수 없어요.

비교 횟수 ≥ log₂(n!) ≈ n·log₂ n − 1.44·n버블 정렬: 최대 n·(n − 1) ÷ 2

직접 만들기

Python 3 · numpy · matplotlib

똑같이 섞인 리스트에 정렬 네 가지를 적용하고 비교 횟수를 세요.

sorting-race.py
"""Sorting race: four algorithms on the same shuffled list, counting their comparisons.
Run: python sorting-race.py   (needs: pip install numpy matplotlib)
"""
import math

import numpy as np
import matplotlib.pyplot as plt

plt.style.use("dark_background")

n = 100
data = list(np.random.default_rng().permutation(n))
count = {"comparisons": 0}


def less(a, b):  # every algorithm compares only through this, so we can count
    count["comparisons"] += 1
    return a < b


def bubble(xs):
    xs = xs[:]
    for end in range(len(xs) - 1, 0, -1):
        for i in range(end):
            if less(xs[i + 1], xs[i]):
                xs[i], xs[i + 1] = xs[i + 1], xs[i]
    return xs


def insertion(xs):
    xs = xs[:]
    for i in range(1, len(xs)):
        j = i
        while j > 0 and less(xs[j], xs[j - 1]):
            xs[j], xs[j - 1] = xs[j - 1], xs[j]
            j -= 1
    return xs


def merge(xs):
    if len(xs) < 2:
        return xs
    left, right = merge(xs[: len(xs) // 2]), merge(xs[len(xs) // 2 :])
    out = []
    while left and right:
        out.append(left.pop(0) if not less(right[0], left[0]) else right.pop(0))
    return out + left + right


def quick(xs):
    if len(xs) < 2:
        return xs
    pivot, rest = xs[0], xs[1:]
    smaller = [x for x in rest if less(x, pivot)]
    return quick(smaller) + [pivot] + quick([x for x in rest if x not in smaller])


results = {}
for name, sort in [("bubble", bubble), ("insertion", insertion), ("merge", merge), ("quick", quick)]:
    count["comparisons"] = 0
    assert sort(data) == sorted(data)
    results[name] = count["comparisons"]
print(results, "| at least log2(n!) =", math.ceil(math.log2(math.factorial(n))))

plt.figure(figsize=(7, 4))
plt.bar(list(results), list(results.values()), color=["#f0abfc", "#c4b5fd", "#5eead4", "#5eead4"])
plt.axhline(math.log2(math.factorial(n)), color="white", linestyle="--", label="log2(n!): no comparison sort does better")
plt.ylabel(f"comparisons for n = {n}")
plt.legend()
plt.show()

# Try: n = 1000 (bubble sort needs about n*n/2, merge sort about n*log2(n))

해 보기: n = 1000으로 바꿔 보세요. 버블 정렬의 횟수는 약 백 배, 병합 정렬은 약 열여섯 배 늘어요.

이 스크립트를 한 줄씩, 연습 문제와 퀴즈로 배우기 → · Google Colab에서 실행 (영어) →

Shorts

출처