수학 배경
학습할 내용
- Amount of Work
- Worst-Case Complexity
- Average Complexity
함수 증가율 분류

이미지 출처: GeeksforGeeks
-
: 함수 보다 증가율이 높거나 같다.
is the set of functions , such that for all -
: 함수 와 같은 증가율을 가진다.
-
: 함수 보다 증가율이 낮거나 같다.
is the set of functions , such that for all
비교하는 방법
보통 최악의 경우를 위해 계산하기 때문에 무한대 상황을 가정한다.
결과값이 무한대로 갈 경우,
또는 최소한 이다.

이미지 출처: GeeksforGeeks
의 성질
- Transitive: 만약 와 이면, 는 transitive하다.
또한 는 transitive하다. - Reflexive:
- Symmetric: 만약 이면, 이다.
이면
와 를 만족한다.
알고리즘과 문제 분석
조건1: Correctness가 증명되어야 한다
- if the preconditions are satisfied
- then the postconditions will be true
- when the algorithm terminates.
조건2: Amount of Work Done
- A measure of work
- input 사이즈
- Basic Operation
- 문제 해결을 위한 근본적인 연산
Worst-Case Complexity
- 이 크기가 인 input 사이즈를 가지고 있다.
- 는 의 원소를 의미한다.
- 는 input 에 대해, basic operation의 수를 의미한다.
- 함수 는 라 정의한다.
Average Complexity
- 는 입력 가 발생할 확률이다.
- 평균 알고리듬 수행시간은 다음과 같이 정의한다.
- 로 알고리듬을 분석한다.
예시
int seqSearch(int E[], int n, int K) {
int ans = -1; // Assume failure
for (int index = 0; index < n; index++) {
if (K == E[index]) {
ans = index; // Success
break; // Done
}
}
return ans;
}
Worst Case
- array에 값이 없을 경우.
- array 마지막 원소가 일 경우. 는 값이 없거나 맨 마지막에 값이 있을 경우이니, 이다.
Average Behavior Analysis
가정
성공하는 경우에는 가 배열의 어느 위치에든 같은 확률로 존재한다고 가정한다. 는 가 번째 위치에서 발견되는 입력을 의미한다.
성공 시 평균 비교 횟수는 다음과 같다.
실패하는 경우에는 배열 전체를 끝까지 확인해야 한다.
따라서 전체 평균 수행시간은 다음과 같다.
만약 성공 확률과 실패 확률이 각각 이라면 다음과 같이 계산된다.
결국 순차 탐색의 평균 수행시간도 에 비례한다.
Best, Worst, Average Case 비교
알고리즘을 분석할 때는 같은 알고리즘이라도 어떤 입력을 받는지에 따라 수행시간이 달라진다. 따라서 보통 다음 세 가지 관점으로 나누어 생각한다.
| 분석 기준 | 의미 | Sequential Search 예시 |
|---|---|---|
| Best Case | 가장 빨리 끝나는 입력 | 첫 번째 원소가 인 경우 |
| Worst Case | 가장 늦게 끝나는 입력 | 가 없거나 마지막에 있는 경우 |
| Average Case | 입력 분포를 고려한 평균 | 의 위치와 실패 확률을 고려한 경우 |
Best Case는 알고리즘의 가장 좋은 상황을 보여주지만, 일반적인 성능을 대표하기 어렵다. Worst Case는 어떤 입력이 들어와도 보장할 수 있는 상한을 제공하기 때문에 알고리즘 분석에서 자주 사용된다. Average Case는 실제 사용 환경을 더 잘 반영할 수 있지만, 입력이 어떤 확률 분포를 따르는지 가정해야 한다.
예제: Nested Loop
다음 코드를 분석해보자.
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
count++;
}
}
내부 반복문은 값에 따라 실행 횟수가 달라진다. 전체 실행 횟수는 다음과 같다.
상수와 낮은 차수 항은 이 커질수록 영향이 작아진다.
따라서 이 코드는 이면서 동시에 이므로 이다.
예제: Binary Search
Binary Search는 정렬된 배열에서 중간 값을 기준으로 탐색 범위를 절반씩 줄인다.
int binarySearch(vector<int>& arr, int target) {
int left = 0;
int right = arr.size() - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) {
return mid;
}
if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
입력 크기가 일 때, 반복할 때마다 탐색 범위는 아래처럼 줄어든다.
번 반복한 뒤 탐색 범위가 1 이하가 된다고 하면 다음을 만족한다.
따라서
양변에 로그를 취하면
즉 Binary Search의 worst-case complexity는 다음과 같다.
증가율 비교
알고리즘의 정확한 실행 시간은 하드웨어, 컴파일러, 구현 방식에 따라 달라질 수 있다. 하지만 입력 크기가 충분히 커지면 증가율이 더 중요한 기준이 된다.
대표적인 증가율은 다음 순서로 커진다.
| Complexity | 대표 예시 |
|---|---|
| 배열 index 접근 | |
| Binary Search | |
| Sequential Search | |
| Merge Sort, Heap Sort | |
| 단순 이중 반복문, Bubble Sort | |
| 부분집합 전체 탐색 | |
| 순열 전체 탐색 |
중요한 것은 작은 입력에서는 알고리즘이 더 빠를 수도 있다는 점이다. Big-O는 실제 시간을 직접 말하는 것이 아니라, 입력 크기가 커질 때의 증가 경향을 설명한다.
알고리즘 분석 절차
알고리즘을 분석할 때는 아래 순서로 접근하면 된다.
- 입력 크기 을 정의한다.
- Basic Operation을 정한다.
- Basic Operation이 몇 번 실행되는지 계산한다.
- Best, Worst, Average 중 어떤 기준으로 분석하는지 정한다.
- 가장 지배적인 항만 남겨 asymptotic notation으로 표현한다.
예를 들어 이중 반복문이라도 항상 인 것은 아니다. 반복문의 범위가 어떻게 변하는지, 중간에 종료되는 조건이 있는지, 입력의 구조가 어떤지까지 함께 봐야 한다.
정리
Optimality Analysis는 단순히 코드를 보고 반복문 개수를 세는 것이 아니다. 알고리즘이 올바르게 동작한다는 correctness를 확인한 뒤, 입력 크기에 따라 필요한 work가 어떻게 증가하는지 분석하는 과정이다.
- 는 upper bound를 의미한다.
- 는 lower bound를 의미한다.
- 는 tight bound를 의미한다.
- Worst Case는 모든 입력에 대해 보장 가능한 성능을 보여준다.
- Average Case는 입력의 확률 분포를 가정해야 계산할 수 있다.
알고리즘 문제를 풀 때도 이 분석이 중요하다. 제한 조건에서 의 크기를 먼저 보고, 허용 가능한 시간복잡도를 예상한 뒤 풀이 방법을 선택해야 한다.