반응형
반응형
해당 글에서는 피보나치의 수열에 이해하고 이를 이용하여 경우의 수를 계산하는 활용방법에 대해서 확인해 봅니다.
1) 피보나치 수열(Fibonacci numbers)
💡 피보나치 수열(Fibonacci numbers) 이란?
- ‘이전 두 항의 합이 다음 항이 되는 수열’을 의미합니다.
- 즉, 첫째 항과 둘째 항이 1이고 이후 모든 항은 모든 항은 바로 앞 두항의 합으로 이루어지는 수열을 의미합니다.
- 피보나치 수열의 예로는 [1, 1, 2, 3, 5, 8, 13, 21, 34,...]과 같은 형태로 구성이 됩니다.
1. 피보나치 수열 계산식
💡 피보나치 수열의 연산식 F(N) = F(N-1) + F(N-2)입니다.
F(0) = 0, F(1) = 1 일 때
F(n) = F(n-1) + F(n-2) (n ≥ 2) 계산식이 됩니다.
- 즉, n번째 항은 n-1번째 항과 n-2번째 항을 더한 값이 됩니다.
2. 피보나치 수열 계산식 상세 이해 : 계단을 오르는 경우의 수
💡 피보나치 수열을 이용하여 계단을 오르는 경우의 수를 계산하는 방법에 대해 이해합니다.
💡 해당 글의 예시는 아래의 링크를 참고하였습니다.
2.1. 계단을 오르는 방법
💡 계단을 오르는 방법은 한 계단을 오르거나 두 계단을 오르는 두 가지 방법이 존재합니다.
2.2. 계단을 오르는 방법 연산식
💡 계단을 오르는 방식은 마지막에 한 계단을 오르는 경우와 마지막에 두 계단을 오르는 경우가 있습니다.
해당 연산은 마지막에 한 계단을 오르는 경우(n-1)의 경우가 되고 마지막에 두 계단을 오르는 경우 (n-2)의 경우가 됩니다.
그래서 피보나치의 수열은 [1, 1, 2, 3, 5, 8, 13, 21, 34,...]인데 계단을 오르는 경우의 수는 처음 두 항이 1, 2 인 피보나치 수열이 되므로 계단을 오르는 경우는 F(n+1)이 됩니다.
2) Java에서 피보나치 수열을 이용한 구현
💡 N개의 계단을 올라갈 때, 한 번에 1 계단 또는 2 계단 씩 오를 수 있다면, 몇 가지 방법으로 올라갈 수 있는지 계산할 수 있습니다.
💡 아래에서는 반복문, 재귀함수, 동적계획법을 이용하여 피보나치 수열 알고리즘을 구현하며 경우의 수를 계산하는 방법에 대해서 확인합니다.
구현 방식 | 장점 | 단점 |
반복문 | 코드가 간결하고 직관적이다 | 피보나치 수열 이외의 문제에 대한 확장성이 낮다 |
재귀함수 | 구현이 쉽고 이해하기 쉽다 | 시간 복잡도가 높아 큰 수를 계산하기 어렵다 |
동적 계획법 | 이미 계산한 값을 활용하여 계산 시간을 줄일 수 있다 | 메모리 사용량이 높아질 수 있다 |
1. 반복문을 이용한 구현 방식
💡 해당 방법은 N개의 계단에 대해서 배열의 사이즈로 지정하고 반복문을 순회하면서 경우의 수를 확인하는 방법입니다.
1. 값이 1인 경우는 즉시 값을 반환하고 종료합니다.
2. 계단을 오르는 방법에는 한 계단과 두 계단 방법으로 첫 번째와 두 번째 배열에 값을 지정합니다.
3. 3번째 요소부터 배열을 순회하면서 이전 계단을 오르는 방법의 수를 더하여 계산을 합니다.
💡 4개의 계단을 오르는 경우 5가지의 경우의 수를 반환합니다.
public class FibonacciStairs {
/**
* 피보나치수를 이용한 경우의 수 계산방법 : 반복문을 이용한 방법
*
* @param n
* @return
*/
public static int calcLoopFibonacci(int n) {
int answer = 0;
// [CASE1] 값이 1인 경우 해당 값을 반환합니다.
if (n <= 1) return n;
// [STEP2] 계단을 오르는 방법에는 한 계단과 두 계단 방법으로 첫번째와 두번째 값을 지정합니다.
int prevPrev = 1;
int prev = 2;
// [STEP3] 3번째 요소부터 배열을 순회하면서 이전 계단을 오르는 방법의 수를 더하여 계산을 합니다.
for (int i = 3; i <= n; i++) {
answer = prev + prevPrev;
prevPrev = prev;
prev = answer;
}
return answer;
}
public static void main(String[] args) {
int n = 4;
System.out.println("계단을 오르는 방식 : " + calcLoopFibonacci(n)); // 계단을 오르는 방식 : 5
}
}
2. 재귀함수를 이용한 구현 방식
💡 해당 방법은 N개의 계단에 대해서 재귀 함수를 이용하여 경우의 수를 확인하는 방법입니다.
1. 해당 방식은 자기 자신을 호출하는 함수를 호출하여 계산을 하여 반환을 하는 방식입니다.
- 재귀 알고리즘으로 구현하는 경우는, 불필요하게 같은 값을 다시 계산하게 되어 계산 속도가 엄청 느리다는 단점이 있습니다.
- 이를 위해 실제로 구현할 때는 한 번 계산한 값을 저장해 두고 재 계산하지 않는 메모라이제이션 기법을 사용하여 구현해야 합니다.
💡 4개의 계단을 오르는 경우 5가지의 경우의 수를 반환합니다.
public class FibonacciStairs {
/**
* 피보나치수를 이용한 경우의 수 계산방법 : 재귀함수를 이용한 방법
* @param n
* @return
*/
public static int calcRecursiveFibonacci(int n) {
if (n <= 1) {
return n;
} else {
return calcRecursiveFibonacci(n - 1) + calcRecursiveFibonacci(n - 2);
}
}
public static void main(String[] args) {
int n = 4;
System.out.println("계단을 오르는 방식 : " + calcRecursiveFibonacci(n + 1)); // 계단을 오르는 방식 : 5
}
}
[ 더 알아보기 ]
💡 재귀함수(Recursive Function)란?
- 자기 자신을 호출하는 함수입니다. 이는 함수 내부에서 동일한 함수를 호출하여 반복적으로 실행되는 함수입니다.
- 재귀함수는 일반적으로 두 가지 주요 요소가 있습니다. 첫째, 재귀함수는 기본 케이스(base case)와 재귀 케이스(recursive case)로 구성됩니다. 둘째, 재귀함수는 스택 메모리를 사용하며, 스택 메모리는 모든 함수 호출의 복사본(로컬 변수 등)을 저장하는 데 사용됩니다.
- 재귀함수를 작성할 때, 재귀 케이스가 한 번은 기본 케이스로 수렴해야 합니다. 그렇지 않으면 무한 루프가 발생할 수 있습니다.
💡 메모이제이션(Memoization) 이란?
- 메모이제이션은 계산한 결과를 저장해 놓고, 동일한 입력 값이 나타날 때마다 저장된 결과를 반환하는 방식입니다.
3. 동적 계획법을 활용하여 구현 방식
💡 해당 방법은 N개의 계단에 대해서 동적 계획법을 활용하여 경우의 수를 확인하는 방법입니다.
1. 값이 1인 경우는 즉시 값을 반환하고 종료합니다.
2. 계단을 오르는 방법에는 한 계단과 두 계단 방법으로 첫번째와 두번째 배열에 값을 지정합니다.
3. 3번째 요소부터 배열을 순회하면서 이전 계단을 오르는 방법의 수를 더하여 계산을 합니다.
4개의 계단을 오르는 경우 5가지의 경우의 수를 반환합니다.
public class FibonacciStairs {
/**
* 피보나치수를 이용한 경우의 수 계산방법 : 동적 계획법을 이용한 방법
*
* @param n
* @return
*/
public static int calcDynamicFibonacci(int n) {
// [STEP1] 계단이 1개일 경우 값을 즉시 1로 리턴합니다.
if (n <= 1) {
return n;
}
// [STEP2] 계단을 오르는 방법에는 한 계단과 두 계단 방법으로 첫번째와 두번째 배열에 값을 지정합니다.
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 2;
// [STEP3] 3번째 요소부터 배열을 순회하면서 이전 계단을 오르는 방법의 수를 더하여 계산을 합니다.
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
public static void main(String[] args) {
int n = 4;
System.out.println("계단을 오르는 방식 : " + calcDynamicFibonacci(n)); // 계단을 오르는 방식 : 5
}
}
[ 더 알아보기 ]
💡 동적 계획법(Dynamic Programming)이란?
- 복잡한 문제를 간단한 하위 문제로 나누어 푸는 방법입니다. 이전에 계산한 값을 저장하고 재활용함으로써 중복 계산을 줄이는 특징이 있습니다. 이 방법은 피보나치 수열 계산뿐만 아니라, 최단 경로, 최장 공통부분 문자열 등 다양한 분야에서 사용됩니다.
💡동적 계획법과 반복문의 차이는 무엇인가?
- 동적 계획법은 이전에 계산한 값을 저장해 두고 필요할 때마다 불러와서 사용한다는 점이 반복문과 차이가 있습니다. 동적 계획법은 계산 시간을 줄일 수 있습니다. 하지만 반복문이 동적 계획법보다 코드가 간결하고 직관적이라는 장점이 있습니다.
3) 참고
💡 해당 문제에서 적용하고 확인해보았습니다.
💡 [참고] 좀 더 다양한 알고리즘에 대해 알고 싶으시면 아래의 글을 참고하시면 도움이 됩니다.
분류 | 알고리즘 | 링크 |
알고리즘 구현시간 | 시간 복잡도, 공간 복잡도, 빅오 표기법 | https://adjh54.tistory.com/186 |
탐색 알고리즘 | 선형탐색 | https://adjh54.tistory.com/193 |
탐색 알고리즘 | 이진탐색 | https://adjh54.tistory.com/187 |
탐색 알고리즘 | 완전탐색 : 기본구조 | https://adjh54.tistory.com/196 |
탐색 알고리즘 | 완전탐색 : 종류 | https://adjh54.tistory.com/197 |
탐색 알고리즘 | 완전탐색: 문제로 이해하기 | https://adjh54.tistory.com/227 |
탐색 알고리즘 | 깊이 우선 탐색(DFS) / 너비 우선 탐색(BFS) | https://adjh54.tistory.com/328 |
알고리즘 설계 방법 | 동적 계획법 알고리즘 | https://adjh54.tistory.com/201 |
알고리즘 설계방법 | 그리디 알고리즘 | https://adjh54.tistory.com/212 |
알고리즘 설계방법 | 분할정복 알고리즘 | https://adjh54.tistory.com/219 |
기타 알고리즘 | 재귀 함수 | https://adjh54.tistory.com/194 |
기타 알고리즘 | 피보나치 수열: 경우의 수 | https://adjh54.tistory.com/183 |
기타 알고리즘 | 유클리드 호제법: 최대공약수, 최대공배수 | https://adjh54.tistory.com/179 |
오늘도 감사합니다. 😀
반응형
'Java > 알고리즘 & 자료구조' 카테고리의 다른 글
[Java/알고리즘] 이진 탐색(Binary Search) 이해하기 (2) | 2023.05.22 |
---|---|
[Java/알고리즘] 시간 복잡도, 공간 복잡도, 빅오 표기법 이해하기 (1) | 2023.05.22 |
[Java/자료구조] 선형구조 - 스택(Stack), 큐(Queue) 이해하기 -2 : 문제로 이해하기 (0) | 2023.05.13 |
[Java/알고리즘] 유클리드 호제법(Euclidean Algorithm) : 최대공약수, 최소공배수 (0) | 2023.05.09 |
[Java/자료구조] 선형구조 - 큐(Queue), 스택(Stack), 덱(Deque) 이해하기 - 1 (2) | 2023.03.04 |