static long fibo1(int n) { // 재귀
callCnt1++;
if (n <= 2) return 1;
return fibo1(n - 1) + fibo1(n - 2);
}
컴퓨터 프로그램을 실행할 때 이전에 계산한 값을 메모리에 저장
매번 다시 계산하지 않도록 함 → 전체 실행속도 향상
동적 계획법의 핵심 기술
메모제이션 가능한 함수
메모이제이션 알고리즘
static long fibo2(int n) { // 메모이제이션 (하향식 접근)
callCnt2++;
if (memo[n] == -1) { // 메모가 안 된 경우
memo[n] = fibo2(n - 1) + fibo2(n - 2);
}
return memo[n];
}
메모이제이션 문제점
메모이제이션 문제점 해결책
분할 정복 | DP |
---|---|
연관 없는 부분 문제로 분할 | 부분 문제 연관 X → 적용 X (점화식) |
부분 문제 → 재귀로 해결 | 부분 문제 → 더 작은 부분 문제 공유 |
부분 문제 해 결합 | 모든 부분 문제 한 번만 계산 |
병합 정렬, 퀵 정렬 | 계산 결과 저장 후 재사용 |
하향식 방법 접근 | 상향식 방법 접근 |
static long fibo3(int n) { // DP (상향식 접근)
long[] D = new long[n + 1];
D[1] = D[2] = 1;
for (int i = 3; i <= n; i++) {
callCnt3++;
D[i] = D[i - 1] + D[i - 2];
}
return D[n];
}
1. 재귀적 접근 (fibo1
)
2. 메모이제이션 (fibo2
)
3. 동적 계획법 (fibo3
)
(1) 재귀적 접근 (fibo1
)
static long fibo1(int n) { // 재귀적 접근
callCnt1++; // 호출 횟수 증가
if (n <= 2) return 1; // n이 1 또는 2일 때는 1 반환
return fibo1(n - 1) + fibo1(n - 2); // n-1항과 n-2항의 합
}
(2) 메모이제이션 (fibo2
)
static long fibo2(int n) { // 메모이제이션 (하향식 접근)
callCnt2++; // 호출 횟수 증가
if (memo[n] == -1) { // 메모가 안 된 경우
memo[n] = fibo2(n - 1) + fibo2(n - 2); // 계산하여 메모에 저장
}
return memo[n]; // 저장된 값 반환
}
(3) 동적 계획법 (fibo3
)
static long fibo3(int n) { // 동적 계획법 (상향식 접근)
long[] D = new long[n + 1]; // n까지 저장할 배열 생성
D[1] = D[2] = 1; // 초기 값 설정
for (int i = 3; i <= n; i++) {
callCnt3++; // 호출 횟수 증가
D[i] = D[i - 1] + D[i - 2]; // 점화식을 사용하여 값 계산
}
return D[n]; // n번째 피보나치 수 반환
}
(4) 메인 함수 (main
)
기능: 사용자 입력을 받아 세 가지 방식으로 피보나치 수를 계산하고, 결과와 수행 횟수를 출력한다.
코드:
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 입력을 받기 위한 BufferedReader 생성
int N = Integer.parseInt(br.readLine()); // 사용자로부터 피보나치 수열의 항을 입력받음
memo = new long[N + 1]; // 메모이제이션을 위한 배열 생성
Arrays.fill(memo, -1); // 메모이제이션 배열 초기화
memo[1] = memo[2] = 1; // 초기 값 설정
System.out.println("메모이제이션 : " + fibo2(N)); // 메모이제이션 결과 출력
System.out.println("메모이제이션 수행 횟수 : " + callCnt2); // 메모이제이션 호출 횟수 출력
System.out.println("재귀 : " + fibo1(N)); // 재귀 결과 출력
System.out.println("재귀 수행 횟수 : " + callCnt1); // 재귀 호출 횟수 출력
System.out.println("DP : " + fibo3(N)); // DP 결과 출력
System.out.println("DP 수행 횟수 : " + callCnt3); // DP 호출 횟수 출력
}
package DP;
import java.io.*;
import java.util.*;
public class FibonacciTest {
static long callCnt1, callCnt2, callCnt3; // 각 방법의 호출 횟수를 저장할 변수
static long[] memo; // 메모이제이션을 위한 배열
static long fibo1(int n) { // 재귀적 접근
callCnt1++; // 호출 횟수 증가
if (n <= 2) return 1; // n이 1 또는 2일 때는 1 반환
return fibo1(n - 1) + fibo1(n - 2); // n-1항과 n-2항의 합
}
static long fibo2(int n) { // 메모이제이션 (하향식 접근)
callCnt2++; // 호출 횟수 증가
if (memo[n] == -1) { // 메모가 안 된 경우
memo[n] = fibo2(n - 1) + fibo2(n - 2); // 계산하여 메모에 저장
}
return memo[n]; // 저장된 값 반환
}
static long fibo3(int n) { // 동적 계획법 (상향식 접근)
long[] D = new long[n + 1]; // n까지 저장할 배열 생성
D[1] = D[2] = 1; // 초기 값 설정
for (int i = 3; i <= n; i++) {
callCnt3++; // 호출 횟수 증가
D[i] = D[i - 1] + D[i - 2]; // 점화식을 사용하여 값 계산
}
return D[n]; // n번째 피보나치 수 반환
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 입력을 받기 위한 BufferedReader 생성
int N = Integer.parseInt(br.readLine()); // 사용자로부터 피보나치 수열의 항을 입력받음
memo = new long[N + 1]; // 메모이제이션을 위한 배열 생성
Arrays.fill(memo, -1); // 메모이제이션 배열 초기화
memo[1] = memo[2] = 1; // 초기 값 설정
System.out.println("메모이제이션 : " + fibo2(N)); // 메모이제이션 결과 출력
System.out.println("메모이제이션 수행 횟수 : " + callCnt2); // 메모이제이션 호출 횟수 출력
System.out.println("재귀 : " + fibo1(N)); // 재귀 결과 출력
System.out.println("재귀 수행 횟수 : " + callCnt1); // 재귀 호출 횟수 출력
System.out.println("DP : " + fibo3(N)); // DP 결과 출력
System.out.println("DP 수행 횟수 : " + callCnt3); // DP 호출 횟수 출력
}
}