Кратко: исходная функция вычисляет числа Фибоначчи динамическим заполнением массива. Что делает код - Заполняет массив длины (n+1)(n+1)(n+1) по рекурренте Fi=Fi−1+Fi−2F_i = F_{i-1} + F_{i-2}Fi=Fi−1+Fi−2 и возвращает FnF_nFn. - Итерации идут от 222 до nnn. Сложность - Временная: O(n)\mathrm{O}(n)O(n) (выполняется n−1n-1n−1 прибавлений). - Память: O(n)\mathrm{O}(n)O(n) (массив размера (n+1)(n+1)(n+1)). Альтернативы по памяти и времени 1) Итеративно с константной памятью (простая оптимизация по памяти) - Память: O(1)\mathrm{O}(1)O(1). - Время: O(n)\mathrm{O}(n)O(n). Пример: def fib_const_space(n): if n <= 1: return n a, b = 0, 1 for _ in range(2, n+1): a, b = b, a + b return b 2) Быстрый удвоенный метод (fast doubling) или бинарное возведение матрицы - Время: O(logn)\mathrm{O}(\log n)O(logn) по числу больших целочисленных умножений (количество арифметических шагов ~log n). - Память: O(logn)\mathrm{O}(\log n)O(logn) рекурсивно или O(1)\mathrm{O}(1)O(1) итеративно. Короткий рекурсивный вариант (fast doubling): def fib_fast_doubling(n): if n == 0: return (0, 1) (a, b) = fib_fast_doubling(n // 2) c = a * (2*b - a) d = a*a + b*b if n % 2 == 0: return (c, d) else: return (d, c + d) # F_n = fib_fast_doubling(n)[0] 3) Формула Бине (приближённо) - Быстро (O(1)\mathrm{O}(1)O(1)), но неточно для больших nnn из‑за потерь точности плавающей точки; не подходит для точного целого результата при больших nnn. Учёт переполнения (overflow) - В Python встроенные целые числа произвольной длины, поэтому переполнение не происходит, но стоимость арифметики растёт с длиной чисел (количество бит ~Θ(n)\Theta(n)Θ(n), т.е. число бит у FnF_nFn примерно nlog2φn\log_2\varphinlog2φ). - В языках со фиксированной арифметикой (C, Java): - использовать библиотеки больших целых (BigInteger, GMP) для точного результата; - либо вычислять по модулю mmm, если нужен результат по модулю (тогда применяйте % m в операциях; fast doubling легко адаптируется). Пример с модулем: def fib_mod(n, m): if n == 0: return 0 a, b = 0, 1 for _ in range(2, n+1): a, b = b, (a + b) % m return b % m Замечания по производительности для очень больших n - Для больших n выгоднее fast doubling: число арифметических операций ~Θ(logn)\Theta(\log n)Θ(logn), но каждое умножение — над очень длинными числами (битовая длина ~Θ(n)\Theta(n)Θ(n)), так что реальная сложность зависит от сложности умножения больших целых. - Если нужен только результат по модулю, берите fast doubling + взятие по модулю, тогда временная сложность выражается в терминах операций над машинными числами и будет близка к O(logn)\mathrm{O}(\log n)O(logn). Выводы кратко - Оригинал: время O(n)\mathrm{O}(n)O(n), память O(n)\mathrm{O}(n)O(n). - Для экономии памяти: итеративно с двумя переменными → память O(1)\mathrm{O}(1)O(1). - Для ускорения: fast doubling или матрицы → время O(logn)\mathrm{O}(\log n)O(logn). - Для переполнения: в Python проблем нет (использовать BigInt), в языках с фикс. размером — использовать BigInteger/GMP или считать по модулю.
Что делает код
- Заполняет массив длины (n+1)(n+1)(n+1) по рекурренте Fi=Fi−1+Fi−2F_i = F_{i-1} + F_{i-2}Fi =Fi−1 +Fi−2 и возвращает FnF_nFn .
- Итерации идут от 222 до nnn.
Сложность
- Временная: O(n)\mathrm{O}(n)O(n) (выполняется n−1n-1n−1 прибавлений).
- Память: O(n)\mathrm{O}(n)O(n) (массив размера (n+1)(n+1)(n+1)).
Альтернативы по памяти и времени
1) Итеративно с константной памятью (простая оптимизация по памяти)
- Память: O(1)\mathrm{O}(1)O(1).
- Время: O(n)\mathrm{O}(n)O(n).
Пример:
def fib_const_space(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(2, n+1):
a, b = b, a + b
return b
2) Быстрый удвоенный метод (fast doubling) или бинарное возведение матрицы
- Время: O(logn)\mathrm{O}(\log n)O(logn) по числу больших целочисленных умножений (количество арифметических шагов ~log n).
- Память: O(logn)\mathrm{O}(\log n)O(logn) рекурсивно или O(1)\mathrm{O}(1)O(1) итеративно.
Короткий рекурсивный вариант (fast doubling):
def fib_fast_doubling(n):
if n == 0:
return (0, 1)
(a, b) = fib_fast_doubling(n // 2)
c = a * (2*b - a)
d = a*a + b*b
if n % 2 == 0:
return (c, d)
else:
return (d, c + d)
# F_n = fib_fast_doubling(n)[0]
3) Формула Бине (приближённо)
- Быстро (O(1)\mathrm{O}(1)O(1)), но неточно для больших nnn из‑за потерь точности плавающей точки; не подходит для точного целого результата при больших nnn.
Учёт переполнения (overflow)
- В Python встроенные целые числа произвольной длины, поэтому переполнение не происходит, но стоимость арифметики растёт с длиной чисел (количество бит ~Θ(n)\Theta(n)Θ(n), т.е. число бит у FnF_nFn примерно nlog2φn\log_2\varphinlog2 φ).
- В языках со фиксированной арифметикой (C, Java):
- использовать библиотеки больших целых (BigInteger, GMP) для точного результата;
- либо вычислять по модулю mmm, если нужен результат по модулю (тогда применяйте % m в операциях; fast doubling легко адаптируется).
Пример с модулем:
def fib_mod(n, m):
if n == 0:
return 0
a, b = 0, 1
for _ in range(2, n+1):
a, b = b, (a + b) % m
return b % m
Замечания по производительности для очень больших n
- Для больших n выгоднее fast doubling: число арифметических операций ~Θ(logn)\Theta(\log n)Θ(logn), но каждое умножение — над очень длинными числами (битовая длина ~Θ(n)\Theta(n)Θ(n)), так что реальная сложность зависит от сложности умножения больших целых.
- Если нужен только результат по модулю, берите fast doubling + взятие по модулю, тогда временная сложность выражается в терминах операций над машинными числами и будет близка к O(logn)\mathrm{O}(\log n)O(logn).
Выводы кратко
- Оригинал: время O(n)\mathrm{O}(n)O(n), память O(n)\mathrm{O}(n)O(n).
- Для экономии памяти: итеративно с двумя переменными → память O(1)\mathrm{O}(1)O(1).
- Для ускорения: fast doubling или матрицы → время O(logn)\mathrm{O}(\log n)O(logn).
- Для переполнения: в Python проблем нет (использовать BigInt), в языках с фикс. размером — использовать BigInteger/GMP или считать по модулю.