본문 바로가기

프로그래밍/알고리즘

시간복잡도에 대해

시간복잡도(time complexity)

- 알고리즘의 자원(resource) 사용량을 분석한다.

- 자원이란 실행 시간, 메모리, 저장 장치, 통신 등을 의미한다.

- 실행시간은 실행 환경에 따라 달라진다.(하드웨어, 운영체제, 언어, 컴파일러 등)

- 실행 시간을 측정하는 대신에 연산의 실행 횟수를 센다.

- 데이터의 크기가 같아도 실제 데이터에 따라 시간 복잡도는 달라질 수 있다.

최악 시간 복잡도(worst-case analysis)

평균 시간복잡도(average-case analysis)


점근적 분석

- 점근적 표기법을 사용한다.

데이터 개수가 n -> ∞일 때 수행시간이 증가하는 growth rate로 시간복잡도를 표현하는 기법이다.

주로 세타(Θ) 표기, 빅오(O) 표기법을 사용한다.

- 점근적 분석 기법은 유일한 분석법도 아니며 또한 가장 유용한 분석법도 아니다. 그럼에도 많은 빈도로 쓰이는 이유로는 상대적으로 가장 간단하고 알고리즘의 실행환경에 의존적이지 않기 때문이다.


EX1 : 상수 시간 복잡도

int sample (int data[], int n) {

int k = n / 2;

return data[k];

}

-> n에 관계없이 상수 시간이 소요되며, 알고리즘의 시간 복잡도는 O(1)이 된다.


EX2 : 선형 시간복잡도

int sum (int data[], int n) {

int sum = 0;

for (int i = 0; i < n; i++)

sum = sum + data[i];

return sum;

}

- sum = sum + data[i];는 위의 예시에서 가장 자주 실행되는 문장이며 실행 횟수는 항상 n번이다.

- 가장 자주 실행되는 문장의 실행횟수가 n번이라면 모든 문장 실행 횟수의 합은 n에 선형적으로 비례하고 모든 연산들 실행횟수의 합도 n에 역시 비례하게 된다.

-> 선형 시간복잡도를 가진다고 표현하며 이 때 시간 복잡도는O(n)이 된다.


EX3 : 선형 시간복잡도 - 순차탐색

int search (int n, int data[], int target) {

for (int i = 0; i < n; i++)

if (data[i] == target)

return i;

return -1;

}

- if (data[i] == target)은 위 예시에서 가장 자주 실행되는 문장이며 탐색하고자 하는 데이터가 가장 뒤에 있을 최악의 경우 실행횟수는 n번이 된다.

-> 시간 복잡도는 O(n)이라고 표기한다.


Quadratic

bool is_distinct(int n, int x[]) {

for (int i = 0; i < n - 1; i++)

for (int j = i + 1; j < n; j++)

if (x[i] == x[j])

return false;

return true;

}

- if (x[i] == x[j])는 위 예시에서 가장 자주 실행되는 문장이며, 최악의 경우 실행횟수가 n(n - 1) / 2번이 된다.

-> 최악의 경우 배열에 저장된 모든 원소 쌍을 비교하게 되어 비교 연산의 횟수는 n(n - 1) / 2이다.

해당 시간 복잡도는 최종적으로 O(n^2)으로 나타낸다.


점근적 표기법

- 알고리즘에 포함된 연산들의 실행 횟수를 표기하는 하나의 기법이다.

- 최고차항의 차수만으로 표시하게 된다.

- 가장 자주 실행되는 연산이나 문장의 실행횟수를 고려하는 것만으로 시간 복잡도를 구할 수 있다.


O - 표기 (빅오 표기)


Ω - 표기 (옴 표기)


Θ - 표기 (세타 표기)


점근 표기법

빅오표기법의 복잡도 순서

- O(1) = Constant

상수형 빅오, 데이터의 양과 상관없이 항상 일정한 실행 시간을 가진다.

ex) 해쉬검색 알고리즘

- O($log n$) = Logarithmic

로그형 빅오, 데이터 양이 많아져도 증가율에 비해 연산횟수의 증가율이 훨씬 낮다

ex) 분류 및 정렬 (이진 검색, 퀵 정렬, 병합 졍렬)

- O($n$) = Linear

선형 빅오, 데이터 양에 따라 정비례한 시간 증가를 보인다.

ex) 선형탐색, 단일 반복문

- O($n log n$) = Log - Linear

선형 로그형 빅오, 데이터 양이 N배 많아지면 실행 시간은 N배보다 조금 더 많아진다. (정비례하지 않다.)

ex) 분류 및 정렬 후 다시 하나로 모을 때, 이진 트리 검색시 로직에 반복문을 덮어씌울 때

- O($n^2$) = Quadratic

데이터 양에 대해 걸리는 시간은 제곱에 비례한다.

ex) 이중 반복문, 삽입정렬, 선택정렬, 버블정렬

- O($n^3$) = Cubic

데이터 양에 대해 걸리는 시간은 세제곱에 비례한다.

ex) 삼중 반복문

- O($2^n$) = Exponential

지수형 빅오, 지수적 증가라는 엄청난 크기의 연산횟수 증가를 보인다. 사용 자체가 비현실적인 편이다.

- O($n!$) = Factorial

팩토리얼 빅오, 지수보다 더한 크기로 증가하여 사실상 이정도까지 시간이 걸리는 것이 힘든 수준이다.

- 상수형부터 지수형까지 순서대로 내려올수록 속도가 느려진다.

빅오 표기법의 시간 복잡도 그래프



빅오 표기법 시간 복잡도표


다항시간(polynomial-time) 알고리즘

- 알고리즘들은 실행 시간이 다항식일 때 효율적이라 할 수 있다.


이진 검색(Binary Search) 알고리즘

- 배열에 데이터들이 오름차순으로 정렬되어 저장된 상태이다.

- 데이터가 연결리스트에 오름차순으로 정렬되어 있다면

연결리스트에서는 가운데 데이터를 O(1)시간에 읽을 수가 없다.

따라서 이진검색을 수행하는 것이 불가능해진다.

- 이진 검색 구현

int binarySearch(int n, char *data[], char *target) {

int begin = 0, end = n - 1;

while (begin <= end) {

int middle = (begin + end) / 2;

int result = strcmp(data[middle], target);

if (result == 0)

return middle;

else if (result < 0)

begin = middle + 1;

else

end = middle - 1;

}

return -1;

}

-> 한 번 비교할 때마다 데이터가 절반씩 줄어들게 된다. 결과적으로 시간 복잡도는 O($log_{2}n$)이 된다.

'프로그래밍 > 알고리즘' 카테고리의 다른 글

Recursion 응용(2)  (0) 2019.05.12
Recursion 응용(1)  (0) 2019.05.10
순환(Recursion)에 대해(3)  (0) 2019.05.09
순환(Recursion)에 대해(2)  (0) 2019.05.08
순환(Recursion)에 대해(1)  (0) 2019.05.08