티스토리 뷰
피보나치 수열
문제: 피보나치 수열에서 n번째 오는 값을 구하시오.
e.g. n이 6일 때, 1, 1, 2, 3, 5, 8 ...
이므로 답은 8
재귀로 아래처럼 간단하게 풀 수 있으나, 이 방법은 O(2^n) 시간 복잡도를 가진다.
def fib(n):
if n == 1 or n == 2:
return 1
return fib(n-2) + fib(n-1)
시간 복잡도를 생각하는 방법은, 아래와 같은 예시를 생각해보면 된다. 위의 피보나치 수열을 구하는 재귀와 밑의 코드의 시간복잡도는 비슷하리라 짐작할 수 있다. 하나의 n에서 두 개의 가지를 뻗어서 자기 자신을 재호출하고 있는 구조이기 때문이다. -1씩 깎이는 거나 -2씩 깎이는거는 나중에 가면 상수값이 되어서 빅오표기법에서는 의미가 없다. (이 영상 의 20분 쯤을 참조)
def recursive(n):
if n <= 1:
return
recursive(n-1)
recursive(n-1)
하나의 노드에서 가지를 두 개씩 뻗어나간다는 것은 트리의 높이가 한 단계 길어질 때마다 현재 노드 수의 * 2씩 된다는 소리이다. 처음에 루트 노드는 1개이니까 1 * 2 * 2 * 2 ... 이렇게 단계가 깊어질 수록 반복될 거고, 즉 트리의 높이가 n이라면 2^n이 될 것이다. (곱하기 2가 n번 반복되니까)
그래서 시간복잡도는 exponential time complexity O(2^n)
가 되는 것이다.
이렇게 되면 n이 50 만 되어도 엄청난 연산 비용이 든다. 이 비용을 줄이기 위해서 memoization을 사용한다.
n이 6일 때 피보나치 수열이 진행되는 모습을 보면, 똑같은 서브트리가 반복되고 있음이 보인다. 루트노드가 3인 서브트리, 루트노드가 4인 서브트리 등이 반복된다. n이 커질 수록 반복되는 서브트리는 더 많아질 것이다. 이렇게 반복될 때마다 계산을 반복하는 것은 매우 비효율적이므로, 어떤 자료구조에다 한 번 연산을 하면 그 결과를 저장해놓기로 한다. 예를 들어 n이 4일 때를 한 번 계산했다면, 오른쪽의 서브트리를 돌 때에는 또 다시 계산하지 않고 저장해놓은 fib(4)의 값을 갖다쓰는 것이다.
딕셔너리를 쓰면 원하는 값을 읽어올 때 O(1) 이므로 딕셔너리에 n을 key로 두고 계산 결과인 fib(n)을 value로 두자.
memo = dict() # memoization을 줄여서 memo
def fib(n, memo):
if n in memo:
return memo[n]
if n == 1 or n == 2:
return 1
memo[n] = fib(n-2, memo) + fib(n-1, memo)
return memo[n]
메모이제이션을 거치면 트리가 이렇게만 뻗어나간다. 대략 2n개 정도의 노드만 거치므로 시간 복잡도는 상수를 무시하고 O(n)
이 된다.
Ref
'Data Structure & Algorithm' 카테고리의 다른 글
[Codility] GenomicRangeQuery (0) | 2021.03.02 |
---|---|
Prefix Sums (0) | 2021.03.02 |
Rotation point 찾기 문제(ft. Binary Search) (0) | 2019.12.03 |
퀵 정렬(Quick sort) (0) | 2019.12.02 |
선택 정렬(Selection Sort) (0) | 2019.11.30 |
- Total
- Today
- Yesterday
- 포인터 변수
- jQuery
- getter
- oracle
- 제네릭스
- 개발 공부
- Session
- this
- 자바
- SQL
- c언어
- Redux
- GIT
- Prefix Sums
- react
- til
- package.json
- 깃
- JavaScript
- 인스턴스
- 리덕스
- Java
- linkedlist
- rxjs
- useEffect
- Data Structure
- 알고리즘
- Conflict
- CSS
- youtube data api
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |