Русский
Русский
English
Статистика
Реклама

Перевод Вычисляем последовательность Фибоначчи за логарифмическое время

В преддверии старта базового кура"Математика для Data Science" приглашаем вас записаться на бесплатный демо-урок, который проведут наши эксперты.

А прямо сейчас предлагаем перевод полезной статьи.


Наивное вычисление знаменитой последовательности Фибоначчи производится за экспоненциальное время. Большинство людей могут сообразить, как сделать это за константное время. Но под силу ли нам улучшить этот показатель? Даже не сомневайтесь.

Что такое последовательность Фибоначчи?

Это классический бесконечный числовой ряд, в котором n-й член представляет собой сумму двух предыдущих членов. Первые два члена - 1 и 1. Третий - 2, четвертый - 3, затем следует 5, 8 и т. д.

Как это вычисляется обычно?

Чаще всего люди склонны использовать наивное рекурсивное решение, которое вызывает функцию на двух предыдущих членах. Очевидно, что это решение невероятно неэффективно, потому что выполняется за экспоненциальное время (каждый вызов функции разветвляется на два отдельных вызова для вычисления n-1 и n-2).

// calculates nth term of fibonacci, 1 indexedProcedure Fib_Naive(int n):    if(n < 3):        return 1;        end_if    return Fib_Naive(n-1) + Fib_Naive(n-2)end_Fib_Naive

Улучшенный подход

Решением получше была бы реализация рекурсивной функции, которая может либо запоминать, либо сохранять временную переменную, содержащую два предыдущих члена.

В качестве альтернативы мы могли бы просто использовать временную переменную (temp) и производить вычисления итеративно, что предпочтительнее, поскольку рекурсия обычно занимает больше времени, чем итерационные решения (потому что вызовы, помещаемые в стек, требуют дополнительных инструкций сборки кода).

// calculates nth term of fibonacci, 1 indexedProcedure Fib(int n):        if(n < 3): return 1;        int prev = 1;        int cur = 1;        for i = 3...n:            int temp = cur;            cur += prev;            prev = temp;        end_forreturn cur;end_Fib

Новый подход

Прежде чем мы начнем, давайте слегка освежим базовые знания из линейной алгебры.

Вспомните единичную матрицу, по главной диагонали которой расположены единицы, а все остальные элементы равны нулю:

Произведение любой матрицы и единичной матрицы равно самой матрице, и наоборот, как показано выше.Произведение любой матрицы и единичной матрицы равно самой матрице, и наоборот, как показано выше.


Если мы поменяем местами две строки единичной матрицы и умножим ее на другую матрицу, это будет эквивалентно перестановке тех же двух строк у другой матрицы:

Такая матрица (в центре) называется матрицей перестановок.Такая матрица (в центре) называется матрицей перестановок.

Теперь предположим, что мы установили единицу в элемент в нижнем правом углу такой матрицы перестановок и умножили ее на вектор 2x1 (a, b). Что произойдет? По правилам умножения матриц, это поменяет местами первые две строки и сделает самый нижний элемент результирующего вектора равным (a + b).

Теперь мы можем вернуться к нашей задаче. Помните наше итеративное вычисление последовательности Фибоначчи? В этом уравнении a представляет предыдущий ((n-1)-й) элемент, а b представляет текущий.

Приведенное выше уравнение умножения матриц делает то же самое.

Оказывается, если мы возведем матрицу слева в n-ю степень и умножим ее на вектор (0, 1), мы получим n-й элемент последовательности Фибоначчи.

Откуда же логарифм?

Два слова: многократное квадрирование. Вместо того, чтобы умножать левую матрицу n раз, мы можем использовать предыдущие произведения умножения матриц, чтобы получить ответ быстрее.

Матрица, возведенная в 8-ю степень, может быть вычислена путем возведения в квадрат исходной матрицы, затем возведения в квадрат для достижения 4-й степени, а затем еще одного возведения в квадрат. Это быстрее, чем 8 раз умножать одну и ту же матрицу.

Таким образом, мы получаем следующий прогноз производительности:

В целях уменьшения путаницы, поскольку реализация на конкретном языке программирования может быть сложной, вот фактическая реализация на Java (а не псевдокод, как в предыдущих примерах).

Здесь нет матриц, но реализация все-равно использует преимущества многократного квадрирования, как обсуждалось ранее, для достижения искомой логарифмической производительности:

public int fib(int N) {        if(N == 0) return 0;        int[] sol = fibHelp(N);                return sol[1];    }        public int[] fibHelp(int n) {        if(n == 1) {            return new int[] {0,1};        }        int m = n/2;                int[] temp = fibHelp(m);                int fPrev = temp[0];        int fCur = temp[1];                int prev = (fPrev * fPrev) + (fCur * fCur);        int cur = fCur * (2 * fPrev + fCur);        int next = prev + cur;                if(n % 2 == 0) {             return new int[] {prev, cur};        }        else {            return new int[] {cur, next};        }    }

*Примечание: Хотя может возникнуть соблазн сказать, что мы значительно сократили время вычислений и что сложность этой реализации может быть классифицирована как логарифмическая, все же есть один нюанс, который мы должны учитывать: временная сложность самой арифметики.

Самый быстрый из известных алгоритмов умножения выполняется за O(n log(n)). Технически количество арифметических операций не соответствует заявленному. Однако глубина нашей рекурсии логарифмическая, как видно из примера кода.


Записаться на открытый вебинар.

Источник: habr.com
К списку статей
Опубликовано: 12.11.2020 20:10:42
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus. онлайн-образование

Big data

Математика

Последовательность фибоначчи

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru