Теорема. Для чисел Фібоначчі виконується формула:
Доказ. Наведемо доказ індукцією.
База індукції. При n = 0 маємо:
або 0 = 1 - 1, що вірно.
Крок індукції. Припустимо, що:
Тоді:
Що і потрібно було довести.
Тоді шукана сума
може бути знайдена як
Обчислення чисел Фібоначчі через формулу Біне
Розглянемо породжувальну функцію для чисел Фібоначчі (F_0 = 0, F_1 = 1):
Звідси:
Розкладемо породжувальну функцію на прості дроби. Знайдемо корені знаменника:
де
Тепер представимо породжувальну функцію у вигляді:
Розв'язуючи систему, отримаємо:
Враховуючи, що
породжувальну функцію можна переписати у вигляді:
Але оскільки
то прирівнюючи коефіцієнти при x^n, отримаємо:
Обчислення чисел Фібоначчі через тотожність
Для чисел Фібоначчі відоме наступне тотожність:
З нього безпосередньо слідують часткові випадки:
якщо m = n, то F_{2n} = F_n \cdot F_{n + 1} + F_{n - 1} \cdot F_n
якщо m = n + 1, то F_{2n + 1} = F_{n + 1} \cdot F_{n + 1} + F_n \cdot F_n
Ці формули дозволяють обчислювати числа Фібоначчі методом "поділяй і володарюй", зводячи обчислення F_n до значень з приблизно вдвічі меншими індексами.
Окремо обробляються базові випадки:
Завдяки такому підходу глибина рекурсії пропорційна log_2n, а часова складність алгоритму становить O(log_2n).
Приклад
Обчислимо кілька чисел Фібоначчі за формулою Біне, працюючи в розширеному полі Z_p (\sqrt{5}) за великим модулем .
Розглянемо на прикладі виконання формул:
якщо m = n, то F_{2n} = F_n \cdot F_{n + 1} + F_{n - 1} \cdot F_n
якщо m = n + 1, то F_{2n + 1} = F_{n + 1} \cdot F_{n + 1} + F_n \cdot F_n
Реалізація алгоритму
Оголосимо константу , за модулем якої будуть проводитися обчислення.
#define MOD 1000000007Оголосимо змінну типу для запам’ятовування вже обчислених чисел Фібоначчі.
map<long long, long long> fib;Функція f обчислює -те число Фібоначчі за модулем .
long long f(long long n)
{
if (n == 0) return 0;
if (n == 1) return 1;
if (n == 2) return 1;Якщо -те число Фібоначчі вже було обчислено раніше і збережено в , воно повертається одразу, без повторних обчислень.
if (fib[n]) return fib[n];Далі використовуємо розкладання числа на:
n = 2k + 1 — непарний випадок: F_n = F_k^2 + F_{k + 1}^2.
n = 2k — парний випадок: F_n = F_k \cdot F_{k + 1} + F_{k - 1} \cdot F_k.
long long k = n / 2;Зберігаємо і повертаємо результат.
if (n % 2 == 1) // n = 2*k + 1
return fib[n] = (f(k) * f(k) + f(k + 1) * f(k + 1)) % MOD;
else // n = 2*k
return fib[n] = (f(k) * f(k + 1) + f(k - 1) * f(k)) % MOD;
}Основна частина програми. Для кожного тесту читаємо вхідні дані і виводимо відповідь.
while (scanf("%lld %lld", &a, &b) == 2)
printf("%lld\n", (f(b + 2) - f(a + 1) + MOD) % MOD);Реалізація алгоритму – формула Біне
Оголосимо константу , за модулем якої будуть проводитися обчислення.
#define MOD 1000000007Оголосимо структуру для зберігання числа виду x + y \sqrt{5}.
struct Num
{
long long x, y; // x + y*sqrt(c)
Num(long long x_ = 0, long long y_ = 0) : x(x_), y(y_) {}
};Реалізуємо множення в полі Z_p (\sqrt{5}). Функція mult повертає добуток чисел та .
Num mult(Num& a, Num& b)
{
Num res;
res.x = (a.x * b.x + 5 * a.y % MOD * b.y) % MOD;
res.y = (a.x * b.y + a.y * b.x) % MOD;
return res;
}Функція pow реалізує піднесення в степінь base^n, де — число в полі Z_p (\sqrt{5}).
Num pow(Num base, long long n)
{
Num res = { 1, 0 }; // одиниця поля
while (n > 0)
{
if (n & 1)
res = mult(res, base);
base = mult(base, base);
n >>= 1;
}
return res;
}Функція modpow обчислює значення a^k~mod~p.
long long pow(long long x, long long n)
{
long long res = 1;
x %= MOD;
while (n > 0)
{
if (n & 1) res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}Функція fib обчислює -те число Фібоначчі.
long long fib(long long n)
{Ініціалізуємо константи φ = 1 + \sqrt{5} та ψ = 1 - \sqrt{5}.
Num phi(1, 1);
Num psi(1, MOD - 1);Обчислюємо степені A = φ^n та B = ψ^n.
Num A = pow(phi, n);
Num B = pow(psi, n);Оскільки
обчислюємо окремо чисельник та знаменник.
Відзначимо, що A_x = B_x (тому A_x - B_x = 0), а коефіцієнт при \sqrt{5} в чисельнику дорівнює A_y - B_y.
long long numerator = (A.y - B.y + MOD) % MOD;Обчислюємо 2^n у знаменнику.
long long denom = pow(2, n);Чисельник та знаменник містять множник \sqrt{5}. Скоротимо дріб на нього. Тепер залишається обчислити результат, який дорівнює
long long denom_inv = pow(denom, MOD - 2);
return numerator * denom_inv % MOD;
}Основна частина програми. Для кожного тесту читаємо вхідні дані і виводимо відповідь.
while (scanf("%lld %lld", &a, &b) == 2)
printf("%lld\n", (fib(b + 2) - fib(a + 1) + MOD) % MOD);