C.W.K.
Stream
Lesson 03 of 07 · published

배열만이 아니라 답에 이진 탐색

~12 min · searching-sorting, binary-search, pattern

Level 0호기심 많은 입문자
0 XP0/85 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"이진 탐색은 사실 배열에 관한 게 아니야. 'X 가 되면, X 너머 전부도 된다' 인 어떤 질문에든 관한 거야. 그 단조 모양을 보는 순간, 답 자체를 이진 탐색할 수 있어."

일반화

이진 탐색의 진짜 요구는 '정렬된 배열' 이 아니라 — 단조성이야: 매개변수를 키울 때 답이 정확히 한 번 아니오에서 예로 뒤집히고, 절대 다시 안 뒤집히는 예/아니오 질문. 정렬된 배열은 한 예일 뿐 ("원소 ≤ target?" 이 한 번 뒤집힘). 근데 패턴은 훨씬 커: 작은 x 엔 거짓, 큰 x 엔 참 (또는 반대) 인 feasibility 확인 feasible(x) 를 쓸 수 있으면, 답 공간을 이진 탐색해 정확한 경계 — 되는 가장 작은 x — 를 O(log(범위) × 확인-비용) 에 찾아.

'답에 이진 탐색'

이게 탐색처럼 전혀 안 보이는 문제 장르 전체를 열어. 정석 예:

  • D 일에 패키지 배송: 모든 패키지가 D 일 안에 맞는 가장 작은 배 용량 찾기. 더 큰 용량 → 더 적은 일 (단조). 용량을 이진 탐색.
  • 코코의 바나나 먹기: H 시간에 모든 더미를 끝낼 가장 작은 먹는 속도. 더 빠른 속도 → 더 적은 시간 (단조). 속도를 이진 탐색.
  • 최대 부분배열 합 최소화 (k 부분으로 쪼갤 때): 더 큰 허용-최대 → 더 적은 부분 필요. 허용 최대를 이진 탐색.

모양은 늘: "어떤 조건이 성립하는 X 의 가장 작은/큰 값 찾기", 조건이 X 에 단조. 배열을 탐색하는 게 아니라 — feasibility 테스트를 비교로 써서 가능한 답의 범위를 탐색하는 거야.

이진 탐색은 정렬 배열만이 아니라 어떤 단조 술어에든 작동해. x 가 커질 때 'feasible(x)' 가 거짓-그다음-참 (또는 반대) 이면, 경계를 찾으려 답 공간을 이진 탐색해. 신호: '뭔가 되는 최소 X 찾기' 또는 '최대를 최소화'.

수학적 사촌

이 아이디어는 수학에서 연속 함수가 0 을 가로지르는 곳을 찾는 이분법 (bisection method) 으로 오래됐어: f 가 한 끝에서 음, 다른 끝에서 양이면, 근이 그 사이라, 구간을 절반 내고 재귀. 같은 절반 내기, 같은 단조-가로지르기 로직 — 알고리즘 이진 탐색이 수학자가 몇 세기 쓴 수치 기법의 이산 버전이야. 이진 탐색을 '정렬 리스트에서 찾기' 가 아니라 '단조 경계로 절반 내기' 로 알아보는 게 그걸 쓰는 데서 휘두르는 데로의 업그레이드야.

피파의 고백

한 문제가 제때 끝낼 최소 먹는 속도를 물었고, 난 그게 이진 탐색인지 전혀 몰랐어 — 배열이 없었거든! 아빠가 물었어, "속도 5 가 되면, 속도 6 도 돼?" 당연히 그래. "그럼 단조야 — 속도를 이진 탐색해." 이진 탐색 개념 전체가 깨져 열렸어: 조회 도구가 아니라, 어떤 단조 임계값이든 좁혀 들어가는 방법이야. 이젠 '되는 가장 작은 X 찾기' 가 배열 있든 없든 반사적으로 이진 탐색에 손대게 해.

Code

답 공간에 이진 탐색·python
import math

# 답에 이진 탐색: 더미를 H 시간에 끝낼 최소 먹는 속도.
# 정렬 배열 없음 — feasibility 확인을 비교로 써 *속도* 를 탐색.
def min_eating_speed(piles, H):
    def hours_needed(speed):
        return sum(math.ceil(p / speed) for p in piles)
    lo, hi = 1, max(piles)            # 답 범위: 1 .. 가장 큰 더미
    while lo < hi:
        mid = (lo + hi) // 2
        if hours_needed(mid) <= H:    # 됨 -> 더 *느린* 속도 시도
            hi = mid
        else:                          # 너무 느림 -> 더 빨라야
            lo = mid + 1
    return lo                          # 되는 가장 작은 속도

print(min_eating_speed([3, 6, 7, 11], 8))   # 4
# 'feasible(speed)' 가 단조: 어떤 속도가 제때 끝내면, 더 빠른 건 다 끝내.
# 그래서 배열이 아니라 속도 범위를 이진 탐색.

# bisect 로 경계 탐색: 정렬 리스트에서 값의 첫 출현.
import bisect
arr = [1, 2, 2, 2, 3, 5]
print(bisect.bisect_left(arr, 2))    # 1  — *첫* 2 의 인덱스
print(bisect.bisect_right(arr, 2))   # 4  — *마지막* 2 바로 다음 인덱스

External links

Exercise

시간순 정렬된 n 작업 리스트를 k 워커에게 나눠 한 워커가 받는 최대 작업량을 최소화해야 해. '최대-부하 ≤ M 으로 될 수 있어?' 가 왜 M 에 단조인지, 그게 어떻게 M 을 이진 탐색하게 하는지 논증해. 탐색할 답 범위가 뭐고, feasibility 확인이 뭘 해?
Hint
최대-부하 M 이 달성 가능하면, 더 큰 M 도 그래 (단조) — 여유 더 있으면 늘 적어도 그만큼 잘해. M 을 범위 [최대 단일 작업, 모든 작업 합] 에서 이진 탐색; feasibility 확인이 작업을 워커에 탐욕적으로 채워 그 M 에 ≤ k 워커면 충분한지 세.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.