이것이 취업을 위한 코딩 테스트다 with 파이썬을 공부하면서 정리한 내용입니다.
중복되는 연산 줄이기
- 메모리 공간을 더 사용하면서 연산 속도를 비약적으로 증가시킬 수 있는 방법이 있음
- 대표적인 방법이 다이나믹 프로그래밍 기법으로 동적 계획법으로 표현하기도 함
- 다이나믹 프로그래밍으로 해결할 수 있는 대표적인 예시로 피보나치 수열이 있음
- 피보나치 수열은 이전 두 항의 합을 현재의 항으로 설정하는 수열
- 점화식은 인접한 항들 사이의 관계식을 의미하며 피보나치 수열의 점화식은 다음과 같음
an+2=f(an+1,an)=an+1+an
- 피보나치 수열에서 첫 번째 항과 두 번째 항의 값은 모두 1이기 때문에 최종적으로 피보나치 수열을 나타낼 때에는 다음과 같이 정의
an=an−1+an−2,a1=1,a2=1
- 프로그래밍에서는 수열을 배열이나 리스트로 표현할 수 있음
- 수학적 점화식을 프로그래밍으로 표현할 때 재귀 함수를 사용하면 간단
- 피보나치 함수는 다음과 같이 작성할 수 있음
def fibo(x):
if x == 1 or x == 2:
return 1
return fibo(x - 1) + fibo(x - 2)
print(fibo(4))
3
- 그런데 피보나치 수열의 소스 코드를 위와 같이 작성하면 f(n) 함수에서 n이 커질수록 수행 시간이 기하급수적으로 늘어나는 문제 발생
- 일반적으로 피보나치 수열은 빅오 표기법을 이용하면 O(2N)이 소요된다고 표현
- f(6)일 때의 호출 과정을 그림으로 나타내면 다음과 같음

- 그림을 보면 동일한 함수가 반복적으로 호출되는 것을 알 수 있음
- f(n)에서 n이 커지면 커질수록 반복해서 호출하는 수가 많아짐
- 피보나치 수열의 점화식을 재귀 함수를 사용해 만들 수는 있지만, 단순히 매번 하도록 하면 문제를 효율적으로 해결할 수 없음
- 이러한 문제는 다이나믹 프로그래밍을 사용하면 효율적으로 해결할 수 있음
- 다이나믹 프로그래밍은 다음 조건을 만족할 때 사용할 수 있음
- 큰 문제를 작은 문제로 나눌 수 있음
- 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일함
- 피보나치 수열은 다이나믹 프로그래밍의 조건을 만족함
- 다이나믹 프로그래밍을 구현하는 방법 중 메모이제이션이 있는데, 한 번 구한 결과를 메모리 공간에 메모해두고 같은 식을 다시 호출하면 메모한 결과를 그대로 가져오는 기법
- 메모이제이션을 구현할 때는 한 번 구한 정보를 리스트에 저장하고 다이나믹 프로그래밍을 재귀적으로 수행하다가 같은 정보가 필요하면 리스트에서 값을 가져오면 됨
d = [0] * 100
def fibo(x):
if x == 1 or x == 2:
return 1
if d[x] != 0:
return d[x]
d[x] = fibo(x - 1) + fibo(x - 2)
return d[x]
print(fibo(99))
218922995834555169026
- 위 소스 코드를 실행하면 99번째 피보나치 수를 구하도록 했음에도 빠르게 정답을 도출
- 다이나믹 프로그래밍을 적용했을 때의 피보나치 수열 알고리즘의 시간 복잡도는 O(N)
- 다이나믹 프로그래밍이란 큰 문제를 작게 나누고, 같은 문제라면 한 번씩만 풀어 문제를 효율적으로 해결하는 알고리즘 기법
- 다이나믹 프로그래밍과 분할 정복의 차이점은 다이나믹 프로그래밍은 문제들이 서로 영향을 미치고 있다는 점
- 재귀 함수를 이용하여 다이나믹 프로그래밍 소스 코드를 작성하는 방법은, 큰 문제를 해결하기 위해 작은 문제를 호출한다고 하여 탑다운 방식이라고 함
- 재귀 함수를 사용하면 컴퓨터 시스템에서는 함수를 다시 호출했을 때 메모리에 적재되는 일련의 과정을 따라야 하므로 오버헤드가 발생할 수 있음
- 재귀 함수 대신에 반복문을 사용한다면 오버헤드를 줄일 수 있음
- 일반적으로 반복문을 이용한 다이나믹 프로그래밍이 더 성능이 좋음
- 반복문을 이용하여 다이나믹 프로그래밍 소스 코드를 작성하는 경우, 작은 문제부터 차근차근 답을 도출한다고 하여 바텀업 방식이라고 함
- 피보나치 수열 문제를 바텀업 방식으로 풀면 다음과 같음
d = [0] * 100
d[1] = 1
d[2] = 1
n = 99
for i in range(3, n + 1):
d[i] = d[i - 1] + d[i - 2]
print(d[n])
218922995834555169026
- 탑다운(메모이제이션) 방식은 하향식이라고도 하며, 바텀업 방식은 상향식이라고도 함
- 다이나믹 프로그래밍의 전형적인 형태는 바텀업 방식
- 바텀업 방식에서 사용되는 결과 저장용 리스트는 DP 테이블이라고 부르며, 메모이제이션은 탑다운 방식에 국한되어 사용되는 표현
- 다이나믹 프로그래밍과 메모이제이션의 개념을 혼용해서 사용하는 경우도 있는데, 엄밀히 말하면 메모이제이션은 이전에 계산된 결과를 일시적으로 기록해 놓은 넓은 개념을 의미하므로, 다이나믹 프로그래밍과는 별도의 개념
- 한 번 계산된 결과를 어딘가에 담아 놓기만 하고 다이나믹 프로그래밍을 위해 활용하지 않을 수도 있음
- 수열은 배열이나 리스트로 표현할 수 있다고 했는데, 메모이제이션은 경우에 따라서 딕셔너리와 같이 다른 자료형을 이용할 수도 있음
- 딕셔너리는 수열처럼 연속적이지 않을 때 유용한데, 예를 들어 an을 계산할 때 a0∼an−1 모두가 아닌 일부의 작은 문제에 대한 해답만 필요한 경우가 있음