← Назад к вопросам
Числа Фибоначчи
1.2 Junior🔥 131 комментариев
#Stream API и функциональное программирование#Кэширование и NoSQL#Основы Java
Условие
Напишите Java-программу для вычисления n-го числа последовательности Фибоначчи.
Последовательность Фибоначчи: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
Каждое число является суммой двух предыдущих.
Примеры
- fib(0) = 0
- fib(1) = 1
- fib(5) = 5
- fib(10) = 55
Требования
- Реализуйте итеративный и рекурсивный варианты
- Для рекурсивного варианта добавьте мемоизацию
- Обработайте случай отрицательных чисел
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подход
Реализуем три варианта: рекурсивный (базовый), рекурсивный с мемоизацией (оптимизированный) и итеративный (самый эффективный). Для рекурсии используем Map для кеширования результатов.
Решение
import java.util.HashMap;
import java.util.Map;
public class FibonacciSolver {
/**
* Метод 1: Наивная рекурсия (без оптимизаций)
* Медленный для больших n из-за повторных вычислений.
*/
public static long fibRecursive(int n) {
if (n < 0) {
throw new IllegalArgumentException("n не может быть отрицательным");
}
if (n == 0) return 0;
if (n == 1) return 1;
return fibRecursive(n - 1) + fibRecursive(n - 2);
}
/**
* Метод 2: Рекурсия с мемоизацией
* Кеширует уже вычисленные значения.
*/
public static long fibMemoized(int n) {
return fibMemoized(n, new HashMap<>());
}
private static long fibMemoized(int n, Map<Integer, Long> memo) {
if (n < 0) {
throw new IllegalArgumentException("n не может быть отрицательным");
}
if (n == 0) return 0;
if (n == 1) return 1;
// Проверяем, есть ли значение в кеше
if (memo.containsKey(n)) {
return memo.get(n);
}
// Вычисляем и сохраняем в кеш
long result = fibMemoized(n - 1, memo) + fibMemoized(n - 2, memo);
memo.put(n, result);
return result;
}
/**
* Метод 3: Итеративный подход (самый эффективный)
* O(n) время, O(1) дополнительная память.
*/
public static long fibIterative(int n) {
if (n < 0) {
throw new IllegalArgumentException("n не может быть отрицательным");
}
if (n == 0) return 0;
if (n == 1) return 1;
long prev2 = 0; // fib(0)
long prev1 = 1; // fib(1)
long current = 1;
// Вычисляем от fib(2) до fib(n)
for (int i = 2; i <= n; i++) {
current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return current;
}
public static void main(String[] args) {
System.out.println("=== Примеры вычисления Фибоначчи ===");
int[] testCases = {0, 1, 5, 10, 15, 20};
for (int n : testCases) {
long recursive = fibRecursive(n);
long memoized = fibMemoized(n);
long iterative = fibIterative(n);
System.out.printf("fib(%2d): recursive=%3d, memoized=%3d, iterative=%3d%n",
n, recursive, memoized, iterative);
}
// Проверка для больших значений
System.out.println("\nБольшие значения (итеративный метод):");
System.out.println("fib(50) = " + fibIterative(50));
System.out.println("fib(100) = " + fibIterative(100));
// Обработка исключений
try {
fibIterative(-5);
} catch (IllegalArgumentException e) {
System.out.println("\nОшибка: " + e.getMessage());
}
}
}
Сложность
- Рекурсия (наивная): O(2^n) время, O(n) память (стек вызовов)
- Рекурсия с мемоизацией: O(n) время, O(n) память (кеш)
- Итеративный подход: O(n) время, O(1) память (только две переменные)
Примеры и тест-кейсы
- Базовые случаи: fib(0)=0, fib(1)=1, fib(2)=1
- Средние значения: fib(5)=5, fib(10)=55
- Большие значения: fib(50)=12586269025 (требует long)
- Граничные случаи: fib(-1) выбросит исключение
Edge cases и типичные ошибки
- Отрицательные индексы: Проверяем с помощью IllegalArgumentException.
- Переполнение: Для очень больших n нужно использовать BigInteger или BigDecimal.
- Повторные вычисления: Наивная рекурсия вычисляет одно число многократно — это худший подход.
- Производительность: Мемоизация существенно ускоряет рекурсию, но итеративный метод все равно быстрее и экономнее по памяти.
- Stack overflow: Глубокая рекурсия без мемоизации может переполнить стек вызовов.
Для собеседования лучше показать все три подхода, объяснив компромиссы между ними.