[백준 2749] 피보나치 수 3 (C#, C++) - soo:bak
작성일 :
문제 링크
설명
이 문제는 매우 큰 수에 해당하는 피보나치 항의 값을 특정 수로 나눈 나머지를 구하는 문제입니다.
단순히 점화식을 그대로 구현할 경우 시간과 메모리 모두 초과되므로, 수학적 성질을 활용해야 합니다.
핵심 아이디어: 피사노 주기(Pisano Period)
- 어떤 수
M
으로 피보나치 수를 나눈 나머지 수열은 항상 유한한 길이의 주기를 가집니다. - 이 주기를 피사노 주기라 하며,
M = 1,000,000
일 때 그 주기는1,500,000
입니다. - 즉, 나머지 수열이 주기적으로 반복되므로 다음이 성립합니다:
여기서:
- \(F(N)\): 피보나치 수의 N번째 항
- \(M\): 나눌 정수 (이 문제에서는 (1,000,000))
- \(P\): 피사노 주기 (이 문제에서는 (1,500,000))
왜 주기가 성립하는가?
피보나치 수열은 다음 점화식을 따릅니다:
\[F(n) = F(n - 1) + F(n - 2)\]여기에 모듈로 연산을 적용하면:
\[F(n) mod M = (F(n-1) + F(n-2)) mod M = (F(n-1) mod M + F(n-2) mod M) mod M\]즉, 피보나치 수열의 모듈로 연산 결과도 점화식의 형태를 유지합니다.
이 연산은 유한한 나머지 값들의 집합 안에서 동작하므로, 언젠가는 같은 나머지 쌍이 다시 등장하게 됩니다.
그 순간부터 수열은 주기적으로 반복되며, 그 주기가 바로 피사노 주기입니다.
따라서:
\[F(N) mod M = F(N mod P) mod M\]이 식은 피보나치 수열의 주기성과 모듈로 연산의 닫힘성을 바탕으로 유도됩니다.
접근법
-
피사노 주기를 활용한 입력 축소 피보나치 수를 어떤 수
M
으로 나눈 나머지를 구할 때는피사노 주기
를 이용해 계산 범위를 줄일 수 있습니다.문제에서
M = 1,000,000
이므로, 피사노 주기는1,500,000
입니다.따라서
F(N) % 1,000,000 = F(N % 1,500,000) % 1,000,000
이므로,실제로는
N % 1,500,000
번째 피보나치 수만 계산하면 됩니다. -
점화식을 이용한 반복 계산 축소된
N' = N % 1,500,000
을 기준으로,피보나치 수의 기본 점화식
F(n) = F(n - 1) + F(n - 2)
을 사용해F(N')
을 계산합니다.- 초기값
F(0) = 0
,F(1) = 1
로 시작합니다. F(n)
을 계산할 때마다1,000,000
으로 나눈 나머지를 저장합니다.
- 초기값
- 배열을 이용한 누적 저장
- 계산 중간의 값을 모두 저장하기 위해 크기
N' + 1
의 배열을 선언합니다. - 각 인덱스에
F(i) % 1,000,000
값을 저장하므로,
수의 크기를 제한하면서도 정확한 나머지 결과를 얻을 수 있습니다.
- 계산 중간의 값을 모두 저장하기 위해 크기
- 최종 출력
- 최종적으로
fibo[N']
값을 출력하면F(N) % 1,000,000
결과와 같습니다.
- 최종적으로
Code
[ C# ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
namespace Solution {
class Program {
static void Main(string[] args) {
const int MOD = 1_000_000;
const int PERIOD = 1_500_000;
var n = ulong.Parse(Console.ReadLine()!) % PERIOD;
var fibo = new int[n + 1];
fibo[0] = 0;
if (n > 0) fibo[1] = 1;
for (ulong i = 2; i <= n; i++)
fibo[i] = (fibo[i - 1] + fibo[i - 2]) % MOD;
Console.WriteLine(fibo[n]);
}
}
}
[ C++ ]
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
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef vector<int> vi;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ull n; cin >> n;
n %= 1'500'000;
vi fibo(n + 1);
fibo[0] = 0;
fibo[1] = 1;
for (ull i = 2; i <= n; i++) {
fibo[i] = fibo[i - 1] + fibo[i - 2];
fibo[i] %= 1'000'000;
}
cout << fibo[n] << "\n";
return 0;
}