Workspace IndexAlgorithms › Day 15

Online Algorithms and Competitive Ratio TODO

Algorithms · Day 15 / 100 · A. Advanced Algorithms & Data Structures (Day 1-19)

Concept

An online algorithm never gets to see the whole input in advance and has to make irrevocable decisions as each request arrives. Its performance is measured not in absolute cost but in competitive ratio, defined as the worst case over all input sequences of (online algorithm's cost) / (offline optimal's cost, knowing the whole input). For cache replacement (paging), with a cache of size k, both LRU and FIFO are k-competitive, and k is also the lower bound on the competitive ratio for any deterministic algorithm — which makes LRU optimal in that class. Allowing randomization, as in the marking algorithm, can lower the expected competitive ratio to logarithmic scale, because it prevents an adversarial input from predicting the algorithm's next move. Problems like ski rental, which are about deciding "when to buy," have a 2-competitive algorithm, and real-time selection problems in the secretary-problem family achieve constant-factor performance guarantees using randomized threshold rules.

Code that has to decide now, without knowing the future — caches, connection pools, real-time bidding, order matching — is everywhere, and average-case intuition alone falls apart under adversarial traffic. Competitive ratio is the one language that quantifies that worst case.

Code & Formula

# Day 15: 온라인 알고리즘과 경쟁비 — 캐시 교체(LRU/FIFO) 대 오프라인 최적(Belady)
# 미래를 모른 채 결정하는 온라인 알고리즘의 비용을 오프라인 최적과 비교해 경쟁비를 측정한다.

def lru_misses(seq, k):
    cache, misses = [], 0
    for x in seq:
        if x in cache:
            cache.remove(x); cache.append(x)
        else:
            misses += 1
            if len(cache) >= k:
                cache.pop(0)
            cache.append(x)
    return misses

def fifo_misses(seq, k):
    cache, order, misses = set(), [], 0
    for x in seq:
        if x not in cache:
            misses += 1
            if len(cache) >= k:
                oldest = order.pop(0)
                cache.remove(oldest)
            cache.add(x); order.append(x)
    return misses

def belady_misses(seq, k):
    cache, misses = [], 0
    for i, x in enumerate(seq):
        if x in cache:
            continue
        misses += 1
        if len(cache) >= k:
            future = seq[i + 1:]
            farthest = max(cache, key=lambda c: future.index(c) if c in future else float("inf"))
            cache.remove(farthest)
        cache.append(x)
    return misses

k = 4
seq = [1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5] * 3   # LRU에 불리하도록 순환 접근 패턴

lru, fifo, opt = lru_misses(seq, k), fifo_misses(seq, k), belady_misses(seq, k)

print(f"캐시 크기 k={k}, 요청 {len(seq)}건")
print(f"LRU miss = {lru}, FIFO miss = {fifo}, 오프라인 최적(Belady) miss = {opt}")
print(f"LRU 경쟁비 ≈ {lru/opt:.2f} (이론 상한 k={k})")
print(f"FIFO 경쟁비 ≈ {fifo/opt:.2f}")

Exercise

Implement LRU, FIFO, and random replacement under the same interface, then construct a cyclic access sequence sized to cache size k that's specifically designed to hit LRU's worst case, and measure whether the cost ratio against the offline optimum (Belady's algorithm) actually approaches k.

Practical Connection

A prediction-market order book that has to decide match-or-reject the instant each order arrives, or an RPC response cache exposed to an adversarial query pattern, are both exactly online decision problems.

If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.


한국어

온라인 알고리즘과 경쟁비 TODO

Algorithms · Day 15 / 100 · A. 고급 알고리즘·자료구조 (Day 1–19)

캐시 교체·실시간 경매

개념

온라인 알고리즘은 입력 전체를 미리 보지 못하고 요청이 도착할 때마다 되돌릴 수 없는 결정을 내려야 하는 알고리즘이다. 성능은 절대 비용이 아니라 경쟁비로 재는데, 임의의 입력열에 대해 (온라인 알고리즘의 비용) / (모든 입력을 아는 오프라인 최적의 비용)의 최악값으로 정의된다. 캐시 교체(페이징)에서는 크기 k인 캐시에 대해 LRU와 FIFO가 k-경쟁적이고, 결정론적 알고리즘의 경쟁비 하한도 k라서 LRU는 이 부류에서 최적이다. 무작위화를 허용하면 마킹 알고리즘처럼 기댓값 기준 경쟁비를 로그 규모로 낮출 수 있는데, 이는 적대적 입력이 알고리즘의 다음 수를 예측하지 못하게 만들기 때문이다. ski rental처럼 '언제 사느냐'를 정하는 문제는 2-경쟁 알고리즘이 있고, 비서 문제류의 실시간 선택 문제는 무작위화된 임계 규칙으로 상수 비율의 성능 보장을 얻는다.

캐시, 커넥션 풀, 실시간 입찰, 주문 매칭처럼 '미래를 모른 채 지금 결정해야 하는' 코드는 어디에나 있는데, 평균 케이스 직관만으로는 적대적 트래픽에서 무너진다. 경쟁비는 그 최악을 정량화해 주는 유일한 언어다.

코드 · 수식

# Day 15: 온라인 알고리즘과 경쟁비 — 캐시 교체(LRU/FIFO) 대 오프라인 최적(Belady)
# 미래를 모른 채 결정하는 온라인 알고리즘의 비용을 오프라인 최적과 비교해 경쟁비를 측정한다.

def lru_misses(seq, k):
    cache, misses = [], 0
    for x in seq:
        if x in cache:
            cache.remove(x); cache.append(x)
        else:
            misses += 1
            if len(cache) >= k:
                cache.pop(0)
            cache.append(x)
    return misses

def fifo_misses(seq, k):
    cache, order, misses = set(), [], 0
    for x in seq:
        if x not in cache:
            misses += 1
            if len(cache) >= k:
                oldest = order.pop(0)
                cache.remove(oldest)
            cache.add(x); order.append(x)
    return misses

def belady_misses(seq, k):
    cache, misses = [], 0
    for i, x in enumerate(seq):
        if x in cache:
            continue
        misses += 1
        if len(cache) >= k:
            future = seq[i + 1:]
            farthest = max(cache, key=lambda c: future.index(c) if c in future else float("inf"))
            cache.remove(farthest)
        cache.append(x)
    return misses

k = 4
seq = [1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5] * 3   # LRU에 불리하도록 순환 접근 패턴

lru, fifo, opt = lru_misses(seq, k), fifo_misses(seq, k), belady_misses(seq, k)

print(f"캐시 크기 k={k}, 요청 {len(seq)}건")
print(f"LRU miss = {lru}, FIFO miss = {fifo}, 오프라인 최적(Belady) miss = {opt}")
print(f"LRU 경쟁비 ≈ {lru/opt:.2f} (이론 상한 k={k})")
print(f"FIFO 경쟁비 ≈ {fifo/opt:.2f}")

연습

LRU, FIFO, 무작위 교체를 같은 인터페이스로 구현하고, 캐시 크기 k에 맞춰 일부러 LRU를 최악으로 만드는 순환 접근열을 생성해 오프라인 최적(Belady)과의 비용비가 실제로 k 근처까지 가는지 측정하라.

실무 · Verex 연결

예측시장 오더북에서 주문이 도착하는 순서대로 즉시 매칭·거절을 결정해야 하는 상황이나, RPC 응답 캐시가 적대적 조회 패턴에 노출되는 상황이 정확히 온라인 결정 문제다.

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 14. 랜덤화·근사 알고리즘16. NP-난해와 환원 →