시간복잡도와 공간복잡도
📂알고리즘시간복잡도와 공간복잡도
정의
주어진 문제를 풀 때의 걸리는 시간을 시간복잡도time Complexity, 메모리 소요를 공간복잡도space Complexity라고 한다.
예시
점근적 표기법은 이들을 표현하는데에 굉장히 유용한 수단이 된다. 시간복잡도에 대한 예시를 살펴보자.
상수 시간 O(1)
n 에 관계없이 끝낼 수 있는 알고리즘으로, 사실상 시간이 걸리지 않는 것이다. 가령 x=[4,3,8,−1,−9,0,5,7,2,6] 에서 세번째 원소를 찾는 알고리즘은 x 가 어떻게 생겨먹었는지에 관심이 없고, 그냥 8 을 반환하면 된다.
선형 시간 O(n)
n 에 비례하는 시간이 걸린다. 가령 x=[4,3,8,−1,−9,0,5,7,2,6] 의 최대값을 찾는 알고리즘은 8 을 세번만에 찾아낼 수 있지만, 그것이 진짜 최대값임을 보장하기 위해 나머지 원소도 모두 체크해야한다. 표현이 이러니까 조금 안 좋은 것 같지만, 사실 선형시간 정도면 꽤 빠른 알고리즘이라고 한다.
이차 시간 O(n2)
n2 에 비례하는 시간이 걸린다. 가령 x=[4,3,8,−1,−9,0,5,7,2,6] 을 크기 순으로 정렬한다고 하면 최대값을 한 번 찾아 맨 끝에 두고, 그 최대값을 뺀 배열에서 또 한 번 최대값을 찾고를 반복하면 된다. 최대값을 한 번 찾을 때마다 n,n−1,⋯,1 번의 시간이 걸리므로 그 합은 등차수열의 합 공식에 따라 k=1∑nk=2n(n+1)=O(n2) 이 된다. 이보다 현명한 방법이 있을 것 같지만, 이보다 미련한 방법은 고민할 필요가 없다.
삼차 시간 O(n3)
n3 에 비례하는 시간이 걸린다. 예로써 n=2k 일 때 n×n 행렬의 곱셈을 생각해볼 수 있는데, A,B∈Rn×n 을 곱한 C=AB 를 구한다고 생각해보자. 그러면 조던 블록Jordan Block 행렬 표현을 사용해 다음과 같은 8개의 2n×2n 행렬의 곱을 구해서 계산할 수 있다.
AB=[A1A3A2A4][B1B3B2B4]=[C1C3C2C4]=C
C1=A1B1+A2B3
C2=A1B2+A2B4
C3=A3B1+A4B3
C4=A3B2+A4B4
이 계산을 계속 반복하면 되는데, 한번 계산하는데 걸리는 시간이 T(n) 이고 반복 외의 수행시간을 c 라고 하면 T(n)=8T(2n)+c 이므로
T(n)=====≈===8T(2n)+c8[8T(4n)+c]+c8[64T(8n)+8c+c]+c83T(8n)+(1+8+82)c83T(8n)+8−183−1c⋮8log2n(T(1)+c)nlog28(T(1)+c)n3(T(1)+c)Θ(n3)
이기 때문이다. 행렬의 곱셈은 수학이 들어간 응용분야라면 거의 예외 없이, 그것도 굉장히 많이 한다. 그런데 n3 은 좀 크다. n=100 만 되어도 무려 n3=106 이 된다. 과연 여기서 더 계산을 줄일 수 있을까? 애초에 쓸데 없는 계산을 하지 않는데 여기서 뭘 어떻게 더 줄일 게 없어보인다. 하지만 슈트라센 알고리즘이라는 놀라운 방법을 사용하면 이걸 더 줄일 수 있다. 이것이 알고리즘의 묘미다.
로그 시간 O(log(n))
엄청 빠르다는 의미다. 가령 정렬 된 배열 sort(x)=[−9,−1,0,2,3,4,5,6,7,8] 에서 0 의 위치를 찾는 문제가 있다고 하자. 간단하게 떠올릴 수 있는 방법은 한 가운데의 원소 하나를 골라 0 보다 큰지 작은지 확인해서 0 보다 크면 오른쪽을 버리고, 작으면 왼쪽을 버리는 식으로 배열을 줄여나가 찾는 것이다. 이를 이진 탐색binary search이라고 한다. 한번 계산하는데 걸리는 시간이 T(n) 이고 비교하는데 걸리는 시간을 c 라고 하면 T(n)=T(2n)+c 이므로
T(n)====≈=T(2n)+c[T(4n)+c]+cT(4n)+2cT(8n)+3c⋮T(1)+clog2nO(log2(n))
이기 때문이다. 로그 밑변환 공식에 따라 O(log(n))) 이 된다고 해도 좋지만, 원래 컴퓨터 공학에서 로그에 별다른 말이 없다면 밑은 보통 e=2.7182… 가 아니라 2 로 두기 때문에 표현에 신경쓸 필요는 없다.
지수 시간 O(2n)
엄청 오래 걸린다는 의미다. 재귀함수를 사용해서 피보나치 수열을 구하는 경우 매우 비효율적인데, 한 번 계산이 n 번째 피보나치 수 an 을 구하기 위한 시간이 T(n) 이고 앞 두 항을 더하는데 걸리는 시간을 c 라고 하면 T(n)=T(n−1)+T(n−2)+c 이므로 황금비 ϕ=1.618… 에 대해서
T(n)=======≈=T(n−1)+T(n−2)+c[T(n−2)+T(n−3)+c]+T(n−2)+c2T(n−2)+T(n−3)+(1+1)c3T(n−3)+2T(n−4)+(1+1+2)c5T(n−4)+3T(n−5)+(1+1+2+3)c8T(n−6)+5T(n−4)+(1+1+2+3+5)c⋮ck=1∑nanϕ−1ϕn−1−1cΩ(ϕn)
이기 때문이다. 마지막 부분은 ϕ≈an−1an 이므로 등비수열의 합 공식을 이용해 근사시킨 것이다. 이런 알고리즘은 현실적으로 사용하는 게 불가능하므로 동적 프로그래밍과 같은 해법을 찾아보거나 거꾸로 이러한 시간 복잡도를 가진다는 점을 이용하면 암호에 응용할 수 있기도 하다.