알고리즘의 성능 평가는 어떻게 할 수 있을까?
알고리즘 성능을 평가하기 위해 '복잡도(Complexity)'의 척도를 사용한다.
그중 시간 복잡도와 공간 복잡도의 개념이 나오며, 동일한 기능을 수행하는 알고리즘이 있을 때 복잡도가 낮을 수록 좋은 알고리즘이라 말한다.
다시 말해, 복잡도는 어떤 알고리즘이 효율적인지를 판단하는 척도
!
시간 복잡도는 특정 알고리즘이 어떤 문제를 해결하는데 걸리는 시간을 의미 한다.
시간 복잡도이지만 실행 시간이 아닌 연산 횟수를 세는 이유
- 모든 OS, IDE, 플랫폼에서 동일한 결과가 나오지 않는다.
- 실행 시간 측정을 위한 또다른 방법이 필요하다.
빅오 표기법(Big-O notation)은 복잡도를 나타내는 점근 표기법 중 가장 많이 사용되는 표기법이다.
알고리즘 효율성을 상한선 기준으로 표기하기 때문에 최악의 경우를 계산하는 방식이다. (알고리즘 효율성은 값이 클수록, 즉 그래프가 위로 향할수록 비효율적임을 의미)
O(1) (Constant)
입력 데이터의 크기에 상관없이 언제나 일정한 시간이 걸리는 알고리즘.(push, pop)
function f(arr,i) {
return arr[i]
}
let result = f([1,2,3,4,5], 1)
console.log(result) // 2
O(log n) (Logarithmic)
입력 데이터의 크기가 커질수록 처리 시간이 로그(log: 지수 함수의 역함수) 만큼 짧아지는 알고리즘.
예를 들어 데이터가 10배가 되면, 처리 시간은 2배. (이진 트리 탐색)
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid; // 찾은 경우 인덱스 반환
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1; // 찾지 못한 경우 -1 반환
}
O(n) (Linear)
입력 데이터의 크기에 비례해 처리 시간이 증가하는 알고리즘.
예를 들어 데이터가 10배가 되면, 처리 시간도 10배(for문)
function f(n){
let count = 0;
for (let i = 0; i < 5*n; i++){
count+=1;
}
return count;
}
입력된 n번에 대하여 count에 n * 5번만큼 더하는 연산을 수행하고 있으므로 f(n) = 5n 이다. 하지만 빅오표기법에서는 모든 상수는 무시한다.
O(n log₂ n) (Linear-Logarithmic)
데이터가 많아질수록 처리시간이 로그(log) 배만큼 더 늘어나는 알고리즘.
예를 들어 데이터가 10배가 되면, 처리 시간은 약 20배(퀵 정렬(quick sort), 병합정렬(merge sort), 힙 정렬(heap Sort))
function mergeSort(arr) {
if (arr.length <= 1) {
return arr; // 배열이 하나 이하의 요소만을 포함하면 정렬이 끝난 것으로 간주
}
const middle = Math.floor(arr.length / 2);
const left = arr.slice(0, middle);
const right = arr.slice(middle);
// 재귀적으로 왼쪽과 오른쪽 배열을 정렬하고 병합
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
// 두 배열을 병합하여 정렬된 결과 생성
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
// 남은 요소들을 결과에 추가
return result.concat(left.slice(leftIndex), right.slice(rightIndex));
}
O(n²) (quadratic)
데이터가 많아질수록 처리시간이 급수적으로 늘어나는 알고리즘.
예를 들어 데이터가 10배가 되면, 처리 시간은 최대 100배(이중 for 문, 삽입정렬(insertion sort), 버블정렬(bubble sort), 선택정렬(selection sort))
function f(n){
for (let i = 0; i < n; i++){
for(let j = 0; j< n; j++ ){
// do something for 1second
}
}
}
O(2ⁿ) (Exponential)
데이터량이 많아질수록 처리시간이 기하급수적으로 늘어나는 알고리즘. (피보나치 수열)
function f(n){
if(n < 1){
return 1
}
return f(n - 1) + f(n - 2)
}
공간 복잡도(Space Complexity)란 작성한 프로그램이 얼마나 많은 공간(메모리)을 차지하느냐를 분석하는 방법.
하지만 예전에 비해 컴퓨터 성능의 발달로 인해 메모리 공간이 넘쳐나다 보니 중요도는 많이 떨어짐.
알고리즘을 실행시키기 위해 필요한 공간(space)는 두 가지로 나눌 수 있다.
시간 복잡도는 얼마나 빠르게 실행되는지, 공간 복잡도는 얼마나 많은 자원(메모리 공간)이 필요한지를 판단한다.
시간 복잡도와 공간 복잡도는 반비례하는 경향이 있어, 보통 알고리즘의 성능을 판단할 때는 시간 복잡도를 위주로 판단한다.