Удивительным образом числа Фибоначчи получаются по формуле Бине где — число Фидия, . Удивительно здесь то, что, несмотря на присутствие иррационального числа Фидия, формула даёт целые числа.
Докажем формулу по индукции. Для этого нужно доказать равенство которое после преобразования превращается в Осталось заметить, что оба числа, и , служат корнями многочлена , так что выражения в скобках в обеих частях равенства обращаются в ноль одновременно. Вот и доказан индуктивный переход. Что же касается базы индукции, то она совершенно очевидна при и .
Увы, это доказательство не приносит полного удовлетворения, поскольку не отвечает на вопрос, откуда вообще берутся подобные удивительные формулы. Мы выясним это в разделе «Вывод формулы Бине».
Обратим внимание на то, что при любом целом неотрицательном . Поэтому, зная, что формула Бине даёт целые числа, можно утверждать, что (здесь забавные скобки означают округление заключённого в них числа до ближайшего целого).
К сожалению, эта простая формула годится для практического вычисления чисел Фибоначчи лишь с оговорками. Дело в том, что иррациональное число Фидия в любой системе счисления требует для точного представления бесконечного количества цифр, и поэтому не может быть представлено точно в памяти компьютера. Это значит, что, проводя вычисления по формуле, мы никогда не можем быть уверены в точности получаемых результатов, если не проведём очень кропотливое и трудоёмкое исследование. Из-за ошибок округления формула может нас подвести.
Мы провели эксперимент, и выяснилось, что вычисление по формуле Бине даёт первую ошибку для числа : вместо правильного значения получилось .
Для вывода формулы Бине мы без колебаний выбрали из миллиона различных методов самый, на наш взгляд, впечатляющий — метод производящих функций.
Производящей функцией числовой последовательности называется выражение (элементы последовательности умножаются на в степени, равной номеру элемента, и затем складываются в бесконечную сумму). Соответствие между последовательностью и её производящей функцией (взаимно-однозначное, между прочим) будем обозначать как .
Пусть нас не волнует то, как именно будет вычисляться эта бесконечная сумма при тех или иных значениях — мы отнесёмся к ней формально. Для наших целей важно лишь то, что определённые операции над последовательностями соответствуют определённым действиям над производящими функциями:
Сделаем небольшое замечание, которое читатели, не знакомые с производными, могут спокойно пропустить. Попытки вычислить как значение при нулевом приводят к неопределённости типа , которая раскрывается по правилу Лопиталя: (штрих означает производную по ). Впрочем, и так понятно, что ( — -я производная по ).
Теперь можно заняться формулой Бине. Заметим, что рекуррентное соотношение превращается в уравнение для производящей функции : откуда, с учётом равенств и , получаем
Замечательно, мы нашли производящую функцию последовательности Фибоначчи, но как получить из неё формулу Бине? Заметим, производящая функция рациональная, то есть отношение двух многочленов. Она может быть представлена в виде суммы простейших дробей вида , где и — некоторые числа. Для этого заметим, что Поэтому откуда для и получаем систему линейных уравнений: Из неё находим Теперь после некоторых преобразований с учётом равенства получаем:
По формуле суммы бесконечной геометрической прогрессии и, таким образом, функция служит производящей для геометрической прогрессии . Следовательно, является производящей для разности геометрических прогрессий со знаменателями и и начальными членами, равными , что и требовалось доказать. Это истинное торжество метода производящих функций.
Пожалуй, самый простой подход к вычислению числа Фибоначчи по его заданному номеру — это вычисление по рекуррентной формуле. Заведём две переменные и для хранения двух соседних чисел Фибоначчи, и присвоим им начальные значения и . Затем в цикле раз сдвинемся по последовательности, заменяя на , а на . Здесь важно отметить, что присваивания в цикле следует производить одновременно, так как после присваивания в выражении окажется новое значение переменной , в то время как там требуется старое. Так что параллельное присваивание — это лучший выбор.
Рекуррентная сводит задачу вычисления к задачам вычисления чисел и . Это соображение приводит нас к рекурсивному решению, в котором вычислению посвящена отдельная процедура с параметром . Она возвращает , если . В противном случае она дважды рекурсивно вызывает сама себя — с параметрами и , после чего возвращает сумму значений, возвращённых при этих вызовах. Проще некуда.
Но, несмотря на изящество и простоту алгоритма, мы получаем программу-катастрофу. Мы убедились в этом, попытавшись вычислить с её помощью всего-то — это заняло на нашем компьютере около 108 секунд. Чтобы дождаться , у нас уже не хватило терпения.
Постараемся разобраться, что же приводит к столь долгой работе программы. На рисунке 9.1. «Рекурсивные вызовы при вычислении чисел Фибоначчи» мы протянули стрелки от одного числа Фибоначчи к другому, если вычисление первого из них повлекло рекурсивный вызов для вычисления другого.
Теперь, глядя на рисунок, подсчитаем, сколько раз вызывалась рекурсивная процедура для вычисления :
1 раз | |
1 раз (для вычисления ) | |
2 раза (1 раз для вычисления и 1 раз для ) | |
3 раза (1 раз для вычисления и 2 раза для ) | |
5 раз (2 раза для вычисления и 3 раза для ) | |
2 раза (для вычисления ) |
Всего для вычисления рекурсивная процедура вызывалась 14 раз, причём для каждого значения параметра в среднем больше двух раз. Это явный перебор, но пока это не производит столь жуткого впечатления. Что же будет при больших ?
Наблюдательный читатель наверняка заметил, что числа в правой колонке таблицы сами образуют (за исключением последнего) последовательность Фибоначчи. Их сумма и есть нужное число рекурсивных вызовов. Как же ведёт себя сумма элементов последовательности Фибоначчи при больших значениях ? Если сами числа растут примерно как геометрическая прогрессия со знаменателем , то для суммы первых чисел хорошее приближение даёт формула суммы геометрической прогрессии: В этой формуле главный член, обеспечивающий быстрый рост — это в числителе. Так что сумма растёт с той же самой скоростью, как геометрическая прогрессия со знаменателем .
К сожалению, рекурсивное решение не оправдало наших ожиданий. Но есть приём программирования, позволяющий улучшить рекурсивную версию. Он устраняет главную проблему, вызванную рекурсией: многократные вызовы рекурсивной процедуры при одних и тех же значениях параметров. Если каждое значение, вычисленное при вызове процедуры, запоминать в таблице вместе со значением параметра, переданного при вызове, то процедура, прежде повторно выполнять то же самое вычисление, могла бы поискать в этой таблице уже готовое решение. Этот приём называется мемоизацией.
Например, в нашей задаче ранее найденные числа Фибоначчи можно было бы запоминать в массиве, записывая их в ячейку с соответствующим индексом. Каждый раз при вызове рекурсивная процедура искала бы решение сначала в массиве, и, если поиск увенчался успехом, возвращала бы найденное значение. И только при неудаче приступала к трудоёмким вычислениям (в частности, к рекурсивным вызовам). Получив результат, она запоминала бы его в массиве, и только после этого возвращала.
Матрицы являются важным инструментом в различных разделах математики. Мы лишь слегка коснёмся этой темы, и ровно в той степени, в которой матрицы понадобятся в нашей задаче.
Матрица представляет собой прямоугольную таблицу, заполненную числами: Каждое число (элемент матрицы) снабжается двумя индексами: первый — номер строки, а второй — столбца. Упомянутую матрицу можно кратко обозначить как
Две матрицы одинакового размера можно поэлементно складывать: Можно определить также поэлементное умножение матрицы на число:
Но по-настоящему полезным для нас будет умножение матриц. Две матрицы можно перемножить, если ширина (количество столбцов) первой матрицы совпадает с высотой (количеством строк) второй. В результате умножения получается матрица, у которой высота берётся от первого сомножителя, а ширина — от второго: Здесь индекс суммирования изменяется от единицы до ширины первой матрицы (или, если угодно, до высоты второй). В словесном виде правило умножения матриц звучит так: чтобы получить элемент с индексами произведения двух матриц, следует перемножить элементы -й строки первой матрицы и -го столбца второй, и сложить полученные произведения. Теперь понятно, откуда берётся требование совпадения ширины первого сомножителя и высоты второго.
Проиллюстрируем умножение матриц на примере:
Ещё раз обращаем внимание читателя, что матричные операции (сложение и умножение) определены лишь для матриц, чьи размеры должным образом соответствуют друг другу. Это условие удовлетворяется, если рассматривать квадратные матрицы одного и того же размера — их можно и складывать, и умножать без препятствий. Особо заметим, что квадратные матрицы можно также возводить в натуральную степень, умножая на себя нужное количество раз. Сложение и умножение матриц естественным образом обобщают сложение и умножение чисел, если отождествлять числа с матрицами размера . Сложение и умножение матриц наследует у чисел все арифметические законы, за исключением одного — перестановочного закона умножения.
Довольно интриговать читателей. Пора уже объяснить, какое отношение матрицы имеют к числам Фибоначчи.
Оказывается, рекуррентное соотношение для чисел Фибоначчи можно записать в матричной форме: В качестве очевидного следствия этого равенства получим В частности, при получим
Возможно, читатели уже догадываются, что нам предстоит быстрое возведение в степень матрицы (о быстром возведении чисел в степень мы писали в главе 8. «Быстрое возведение в степень»). Для получения -го числа Фибоначчи нужно возвести эту матрицу в -ю степень и у получившейся матрицы взять элемент, стоящий в правом верхнем углу — это результат умножения на матрицу . Дело в том, что в алгоритме быстрого возведения в степень нет ничего такого, что помешало бы быстро возводить в степень матрицы. Остался единственный вопрос, на который необходимо ответить, чтобы адаптировать алгоритм быстрого возведения в степень для матриц: что считать результатом возведения квадратной матрицы в нулевую степень? Каков аналог единицы в мире матриц? Это единичная матрица, всюду заполненная нулями, за исключением главной диагонали, идущей с северо-запада на юго-восток, и содержащей единицы: Легко проверить, что умножение такой матрицы на любую матрицу подходящего размера (как слева, так и справа) ничего не изменяет: .