2026. 6. 12. 11:29ㆍProgrammers
문제
F(0) = 0
F(1) = 1 일때 F(n) = F(n-1) + (n-2)인 피보나치 수열에서 n번째 수를 1234567로 나눈 나머지를 return하는 문제이다.

풀이 아이디어
처음엔 재귀함수로 접근했다.
int Fib(int n) {
if(n < 2) return n;
return Fib(n-1) + Fib(n-2);
}
재귀 방식은 구현이 직관적이라서 사용하기 좋지만 시간복잡도가 O(2^n)이라 n이 커질수록 급격히 느려진다.
Fib(5)만 호출해도 Fib(3)이 2번 Fib(2)가 3번이 중복 호출되어 매우 비효율적이다.
Fib(5)
├── Fib(4)
│ ├── Fib(3) ← 중복
│ └── Fib(2) ← 중복
└── Fib(3) ← 중복
└── Fib(2) ← 중복
n이 최대 100,000인 이 문제에서 재귀함수를 쓰면 시간 초과가 발생할 수 있다.
그래서 반복문으로 바꿔 O(n)으로 해결하려고 한다.
문제 풀이
#include <string>
#include <vector>
using namespace std;
int Fib(int n)
{
if(n < 2) return n;
int a = 0, b = 1;
for(int i = 2; i <= n; ++i)
{
int c = (a + b) % 1234567;
a = b;
b = c;
}
return b % 1234567;
}
int solution(int n) {
return Fib(n);
}
나머지를 매번 나누는 이유?
문제에서는 "n번째 피보나치 수를 1234567로 나눈 나머지를 return" 하라고 했다.
그래서 처음엔 n번째 수까지 피보나치를 적용한 후 마지막에 나눈 수를 return했다.
하지만 이렇게 되면 오버플로우가 발생하게 된다.
n = 100,000일 때 피보나치수는 천문학적으로 커져 int는 물론, long long으로도 표현이 불가능한 숫자가 된다.
중간 계산 값이 범위를 넘어버리면 이미 틀린 값이 되어버려 오답이 된다.
수학적으로
(a + b) % m = ((a % m) + (b % m)) % m
이 성립하기 때문에 매번 나눠도 최종 결과가 동일하다.
// 마지막에 한 번만 나누면
Fib(100000) = 천문학적으로 큰 수 → overflow → 오답
// 매번 나누면
c = (a + b) % 1234567 // 매 반복시 1234567 미만으로 유지 → 정답
코드 흐름
1. 초기값 설정
int a = 0, b = 1;
재귀함수 기준 a는 F(n-2), b는 F(n-1)을 의미한다.
재귀 함수의 조건 중 하나인 명확한 이탈 조건을 명시해야 하기 때문에 시작값인 F(0) = 0과 F(1) = 1로 초기화해준다.
2. 반복문으로 피보나치 계산
for(int i = 2; i <= n; ++i)
{
int c = (a + b) % 1234567;
a = b;
b = c;
}
매 반복마다 세 변수가 한 칸씩 앞으로 당겨진다.
| 변수 | 역할 |
| a | F(n-2) |
| b | F(n-1) |
| c | F(n) = a + b |
예시로 Fib(5)라고 했을 때,
초기: a=0(F0), b=1(F1)
i=2: c = a+b = 0+1 = 1 → a=b=1, b=c=1 // a=F(1), b=F(2)
i=3: c = a+b = 1+1 = 2 → a=b=1, b=c=2 // a=F(2), b=F(3)
i=4: c = a+b = 1+2 = 3 → a=b=2, b=c=3 // a=F(3), b=F(4)
i=5: c = a+b = 2+3 = 5 → a=b=3, b=c=5 // a=F(4), b=F(5)
return b = 5
매 반복마다 a → b → c 순서로 한 칸씩 앞으로 당겨지는 구조이다.
c를 먼저 계산한 뒤 a=b, b=c 순서로 대입해야 한다. 순서가 바뀌면 a가 덮어씌워져서 c계산이 틀어진다.
3. 결과 반환
return b % 1234567;
최종 결과도 한 번 더 나눠서 반환한다.
트러블슈팅
나머지 연산을 조건부로만 적용해서 몇 개의 케이스에서 오류가 났다.
처음 코드
if(a > 1234567)
{
a %= 1234567;
b %= 1234567;
}
int c = a + b;
여기서 두 가지 문제가 있었다.
문제 1 - 조건이 틀림
a > 1234567 일 때만 나머지 연산을 하게 되면, a가 1234566, b가 그보다 더 큰 수이면 더했을 때 범위를 벗어나게 되어 매번 나눠주는 것이 좋다.
문제 2 - 나누는 시점이 늦다.
// a와 b를 먼저 나누고
a %= 1234567;
b %= 1234567;
// 그 다음 더함
int c = a + b; // 여기서도 overflow 가능
a와 b를 따로따로 나눠도 더하는 순간 다시 범위를 넘을 수 있었다.
덧셈과 나머지 연산을 동시에 처리해야 한다.
int c = (a + b) % 1234567;
핵심포인트
- 재귀 방식은 O(2^n)으로 n이 클 때 시간 초과가 발생한다. 반복문으로 O(n)으로 줄인다
- (a + b) % m = ((a % m) + (b % m)) % m이 성립하므로 매번 나눠도 최종 결과가 동일하다
- 매번 나머지 연산을 적용해 overflow를 방지한다
'Programmers' 카테고리의 다른 글
| <프로그래머스 C++> 예상 대진표 풀이 ( ceil() ) (0) | 2026.06.16 |
|---|---|
| <프로그래머스 C++> 카펫 풀이 (브루트포스) (0) | 2026.06.15 |
| <프로그래머스 C++> 이진 변환 반복하기 풀이 (0) | 2026.06.11 |
| <프로그래머스 C++> Jaden Case 문자열 만들기 풀이( toupper(), tolower() ) (0) | 2026.06.10 |
| <프로그래머스 C++> 최댓값과 최솟값 풀이(stringstream, climits) (0) | 2026.06.09 |