Найти числа фибоначчи в массиве php
Число Фибоначчи в массиве
Нам дан массив, и наша задача — проверить, присутствует ли элемент массива в ряду Фибоначчи или нет. Если да, то напечатайте этот элемент.
Примеры:
// Программа CPP для поиска номеров серий Фибоначчи
// в данном массиве.
#include
using namespace std;
// Функция для проверки номера
// идеальный квадрат или нет
bool isPerfectSquare( int num)
// Функция для проверки, если число
// в Фибоначчи или нет
void checkFib( int array[], int n)
int n = sizeof (array) / sizeof (array[0]);
// Java-программа для поиска номеров рядов Фибоначчи
// в данном массиве
// Функция для проверки номера
// идеальный квадрат или нет
static boolean isPerfectSquare( int num)
int n = ( int )(Math.sqrt(num));
// Функция для проверки, если число
// в Фибоначчи или нет
static void checkFib( int array[], int n)
System.out.println( «None Present» );
public static void main(String[] args)
int n = array.length;
// Предоставлено Прамод Кумар
# Python программа для поиска
# Номера серий Фибоначчи
# в данном массиве.
n = int (math.sqrt(num))
# Функция для проверки, если номер
# в Фибоначчи или нет
def checkFib(array, n):
if (isPerfectSquare( 5 * array[i] * array[i] + 4 ) or
print ( «None present» );
# Этот код добавлен
# Анант Агарвал.
// C # программа для поиска ряда Фибоначчи
// числа в данном массиве
// Функция для проверки номера
// идеальный квадрат или нет
static bool isPerfectSquare( int num)
int n = ( int )(Math.Sqrt(num));
// Функция для проверки, если число
// в Фибоначчи или нет
static void checkFib( int [] array, int n)
if (isPerfectSquare(5 * array[i] * array[i] + 4) ||
Console.WriteLine( «None Present» );
public static void Main()
int n = array.Length;
// Этот код предоставлен Sam007
// PHP программа для поиска
// Серийные числа Фибоначчи
// в данном массиве.
// Функция для проверки
// число идеальное
// квадрат или нет
// Функция для проверки
// если число
// в Фибоначчи или нет
echo «None present\n» ;
$array = array (4, 2, 8, 5, 20,
// Этот код предоставлен mits.
Выход:
Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.
Фибоначчи на собеседовании
1. Быть проще, и люди к вам потянутся.
Самое простое решение — это банальный цикл.
Шутка. Разумеется, так писать не нужно — если, конечно, вы не собеседуетесь на должность штатного обфускатора.
У вас кончились талончики на переменные? cypok
Ладно, ладно, для ещё большей читаемости напишем так:
Это — вариант классический, простой и элегантный. Но, возможно, вы хотите продемонстрировать своё знание ещё каких-то концепций? Например…
2. Чтобы понять рекурсию, надо понять рекурсию
Например, да, вы можете продемонстрировать, что умеете в рекурсию. Например, так:
Запомните этот вариант. Так делать не стоит. Не следует. Нельзя. Никогда. Это хуже, чем пинать щеночков, и сравнимо с небольшим холокостом.
Возможно, вы спросите, почему. В таком случае просто запустите этот код и попытайтесь посчитать, скажем, пятидесятое число Фибоначчи. Полагаю, вы ощутите некую задержку. Шутка. Полагаю, если вы запускаете этот код не на суперкомпьютере, то попросту не дождётесь результата. При том, что простой, не рекурсивный код из предыдущих примеров посчитает пятидесятый член последовательности Фибоначчи быстрее, чем вы успеете произнести слово «пятьдесят» или любой его слог.
Выражаясь грубым языком O-нотации, такое решение имеет временную сложность O(e n ). То есть — время выполнения этой функции растёт экспоненциально при увеличении n. То есть — когда n увеличивается на, время выполнения увеличивается в. Грубо говоря, если fib(45) вам пришлось ждать час, то fib(46) вы будете ждать два часа, fib(47) — 4 часа, и так далее. Я разжёвываю так подробно, чтобы каждый читатель, даже верстальщик, впервые попробовавший свои силы в написании скриптов, мог осознать ужас ситуации.
Это правильно, но слишком грубо. Можно получить более точную оценку числа вызов функции
(1+sqrt(5)) fib(n) и красивое замечание «Для вычисления числа Фибонначи наивным рекуррентным методом понадобится вызовов функции в 3.2 раза больше чем само число Фибонначи». Taus
И мы получаем ещё один метод его вычисления. Надо просто запустить наивный рекурректный метод, подсчитать количество вызовов функции и разделить на 3.2! Cerberuser
Если от вас на собеседовании потребуют рекурсивного решения этой задачи, скорее всего, это ловушка. «Правильная» рекурсия, работающая за линейное время, может выглядеть, например, так:
Подводя итог: несмотря на то, что числа Фибоначчи являются классическим, хрестоматийным примером рекурсии, в действительности это не самый удобный случай для применения рекурсии. Но можно попробовать блеснуть ещё какими-нибудь знаниями.
3. Мемная функция
Существует волшебный способ, превращающий чудовищно неэффективное решение из прошлого параграфа в потенциально очень быстрое (хотя и не лишённое проблем). Имя ему — мемоизация. А если говорить по-русски — мы просто запоминаем результаты предыдущих вызовов вместо того, чтобы вычислять их заново.
Так вот, теперь предыдущие значения будут вычисляться по одному разу, а при их повторном запросе — просто браться из кэша. Представляете, во сколько раз быстрее мы сможем теперь вычислить сорок пятое число Фибоначчи? Серьёзно, как вы думаете, во сколько?
Замечательно! Теперь первый вызов fib(45) отработает со скоростью, сравнимой с версией с циклом. А дальнейшие вызовы вообще сработают за константное время… Оп! Опять обманул. Получение значения свойства объекта по ключу — это операция быстрая, но всё-таки O(1) только в среднем, в худшем случае она может деградировать до O(n). Чтобы стало совсем хорошо, в нашем случае мы можем сменить тип cache с объекта на массив.
Разумеется, не стоит также забывать, что мемоизация требует памяти. И пока мы уменьшаем сложность по времени, сложность по памяти растёт с O(1) до O(n).
Как ещё мы можем выпендриться? Например, продемонстрировав своё глубокое знание математики
4. Мистер Бине
Существует особая прекрасная наука о том, как рекуррентные соотношения превращать в явные формулы. Здесь мы не будем вдаваться в её детали. Скажем лишь, что для чисел Фибоначчи с помощью достаточно несложных рассуждений можно вывести следующую формулу, известную как формула Бине:
Однако довольно языка математики, запишем это на языке JavaScript:
Прогоним на первых нескольких числах. Замечательно, кажется, всё работает. Вот 13, вот 21, вот 34, вот… 54.99999999999999?
Да, разумеется, такой результат закономерен. Формула Бине точна математически, но компьютер оперирует дробями конечной точности, и при действиях над ними может накопиться ошибка, что и произошло в данном случае. Однако мы можем всё исправить. Зная, что вычитаемое в числителе всегда будет маленьким по модулю, мы можем упростить формулу до следующего состояния:
Здесь странные недоделанные квадратные скобки означают ближайшее целое число, то есть — округление. Перепишем наш код:
Да, так гораздо лучше. Мы сможем увидеть и 55, и 89, и даже моё любимое число Фибоначчи — 144 (которое я люблю за то, что оно равняется двенадцати в квадрате). Всё будет хорошо до числа за номером 76. Которое должно быть равно 3416454622906707, а наша функция вычислит 3416454622906706. Потому что проблема ограниченной точности дробных чисел никуда не делась, мы просто затолкали её поглубже и надеялись, что она не всплывёт. Как показывает данный пример — надеялись напрасно.
На самом деле мы можем сделать ещё кое-что, чтобы спасти этот метод. Но об этом ниже. А пока — шутки в сторону. Поговорим о методе суровом, хардкорном и брутальном.
5. Следуй за белым кроликом.
Говорят, если у вас есть проблема и вам пришла в голову идея, что можно решить её с помощью регулярных выражений, то теперь у вас две проблемы. Матрицы — это регулярные выражения наоборот. Многие проблемы, если их переформулировать на языке матриц, решаются просто сами собой.
Что касается чисел Фибоначчи, для них на матричном языке можно записать вот такое очевидное тождество:
То есть если взять пару подряд идущих чисел Фибоначчи и умножить их на такую вот незамысловатую матрицу, мы получим следующую пару. А отсюда логично следует вывод: если мы возьмём пару из нулевого и первого числа Фибоначчи, то есть нуля и единицы, и умножим их на эту матрицу в энной степени, мы получим пару из энного и эн плюс первого числа Фибоначчи. То есть, говоря по-человечески:
Можно это ещё немного упростить, отказавшись от векторов. На самом деле все необходимые значения содержатся в самой матрице:
Замечательно, не правда ли? Осталось понять, нафига попу гармонь, если он не филармонь. В смысле — зачем такие сложности на ровном месте. А ответ прост — быстрое возведение в степень.
Программист скажет. что он это число помнит наизусть, и ничего умножать не нужно. Однако вопросы мемоизации мы рассмотрели выше.
Так вот, быстрое возведение в степень применимо и к матрицам, и таким образом позволяет уменьшить асимптотическую временную сложность нашей функции с O(n) до O(log n). И это очень круто — если, конечно, нам действительно так важна эта сложность. Давайте напишем код:
Вот мы и получили самый быстрый алгоритм на Диком Западе. И его, в отличие от большинства предыдущих, можно неиронично продемонстрировать на собеседовании. А в каких-нибудь математико-ёмких местах именно его от вас и будут ждать.
Я обещал ремарку относительно того, как же нам спасти метод, основанный на формуле Бине. Ответ кроется вот в этой моей статье. Там я для нужд народного хозяйства написал специальный класс корень-из-пяти-рациональных чисел, которые могут без потери точности хранить результаты арифметических действий над целыми числами и корнем из пяти. Можно взять этот класс, дополнить его методом округления и использовать для поиска чисел Фибоначчи по формуле Бине. А затем впрыснуть закись азота, применив быстрое возведение в степень.
И что самое интересное: если внимательно посмотреть, какие числа будут получаться в процессе, какие будут выполняться операции, то станет понятно, что этот метод — это то же самое матричное умножение, только под другим фасадом. Разница лишь в том, храним мы числа в двумерных массивах или в полях объекта специального класса.
На этом всё. Если вы считаете, что я упустил ещё какие-то интересные способы найти никому не нужные числа, обязательно напишите об этом в комментариях.
Есть ещё такой способ как fast doubling. Работает как и матричное умножение за O(log), но с меньшей константой в асимптотике (и на практике). Если кратко, то там используется две формулы, опираясь на которые можно быстро рекурсивно откатываться к вдвое меньшим индексам:
Реализация, кстати, получается довольно компактная.
5 способов вычисления чисел Фибоначчи: реализация и сравнение
Введение
Программистам числа Фибоначчи должны уже поднадоесть. Примеры их вычисления используются везде. Всё от того, что эти числа предоставляют простейший пример рекурсии. А ещё они являются хорошим примером динамического программирования. Но надо ли вычислять их так в реальном проекте? Не надо. Ни рекурсия, ни динамическое программирование не являются идеальными вариантами. И не замкнутая формула, использующая числа с плавающей запятой. Сейчас я расскажу, как правильно. Но сначала пройдёмся по всем известным вариантам решения.
Код предназначен для Python 3, хотя должен идти и на Python 2.
Для начала – напомню определение:
Замкнутая формула
Решаем квадратное уравнение:
Откуда и растёт «золотое сечение» ϕ=(1+√5)/2. Подставив исходные значения и проделав ещё вычисления, мы получаем:
что и используем для вычисления Fn.
Хорошее:
Быстро и просто для малых n
Плохое:
Требуются операции с плавающей запятой. Для больших n потребуется большая точность.
Злое:
Использование комплексных чисел для вычисления Fn красиво с математической точки зрения, но уродливо — с компьютерной.
Рекурсия
Самое очевидное решение, которое вы уже много раз видели – скорее всего, в качестве примера того, что такое рекурсия. Повторю его ещё раз, для полноты. В Python её можно записать в одну строку:
Хорошее:
Очень простая реализация, повторяющая математическое определение
Плохое:
Экспоненциальное время выполнения. Для больших n очень медленно
Злое:
Переполнение стека
Запоминание
У решения с рекурсией есть большая проблема: пересекающиеся вычисления. Когда вызывается fib(n), то подсчитываются fib(n-1) и fib(n-2). Но когда считается fib(n-1), она снова независимо подсчитает fib(n-2) – то есть, fib(n-2) подсчитается дважды. Если продолжить рассуждения, будет видно, что fib(n-3) будет подсчитана трижды, и т.д. Слишком много пересечений.
Поэтому надо просто запоминать результаты, чтобы не подсчитывать их снова. Время и память у этого решения расходуются линейным образом. В решении я использую словарь, но можно было бы использовать и простой массив.
(В Python это можно также сделать при помощи декоратора, functools.lru_cache.)
Хорошее:
Просто превратить рекурсию в решение с запоминанием. Превращает экспоненциальное время выполнение в линейное, для чего тратит больше памяти.
Плохое:
Тратит много памяти
Злое:
Возможно переполнение стека, как и у рекурсии
Динамическое программирование
После решения с запоминанием становится понятно, что нам нужны не все предыдущие результаты, а только два последних. Кроме этого, вместо того, чтобы начинать с fib(n) и идти назад, можно начать с fib(0) и идти вперёд. У следующего кода линейное время выполнение, а использование памяти – фиксированное. На практике скорость решения будет ещё выше, поскольку тут отсутствуют рекурсивные вызовы функций и связанная с этим работа. И код выглядит проще.
Это решение часто приводится в качестве примера динамического программирования.
Хорошее:
Быстро работает для малых n, простой код
Плохое:
Всё ещё линейное время выполнения
Злое:
Да особо ничего.
Матричная алгебра
И, наконец, наименее освещаемое, но наиболее правильное решение, грамотно использующее как время, так и память. Его также можно расширить на любую гомогенную линейную последовательность. Идея в использовании матриц. Достаточно просто видеть, что
А обобщение этого говорит о том, что
Два значения для x, полученных нами ранее, из которых одно представляло собою золотое сечение, являются собственными значениями матрицы. Поэтому, ещё одним способом вывода замкнутой формулы является использование матричного уравнения и линейной алгебры.
Так чем же полезна такая формулировка? Тем, что возведение в степень можно произвести за логарифмическое время. Это делается через возведения в квадрат. Суть в том, что
где первое выражение используется для чётных A, второе для нечётных. Осталось только организовать перемножения матриц, и всё готово. Получается следующий код. Я организовал рекурсивную реализацию pow, поскольку её проще понять. Итеративную версию смотрите тут.
Хорошее:
Фиксированный объём памяти, логарифмическое время
Плохое:
Код посложнее
Злое:
Приходится работать с матрицами, хотя они не так уж и плохи
Сравнение быстродействия
Сравнивать стоит только вариант динамического программирования и матрицы. Если сравнивать их по количеству знаков в числе n, то получится, что матричное решение линейно, а решение с динамическим программированием – экспоненциально. Практический пример – вычисление fib(10 ** 6), числа, у которого будет больше двухсот тысяч знаков.
n = 10 ** 6
Вычисляем fib_matrix: у fib(n) всего 208988 цифр, расчёт занял 0.24993 секунд.
Вычисляем fib_dynamic: у fib(n) всего 208988 цифр, расчёт занял 11.83377 секунд.
Теоретические замечания
Не напрямую касаясь приведённого выше кода, данное замечание всё-таки имеет определённый интерес. Рассмотрим следующий граф:
Подсчитаем количество путей длины n от A до B. Например, для n = 1 у нас есть один путь, 1. Для n = 2 у нас опять есть один путь, 01. Для n = 3 у нас есть два пути, 001 и 101. Довольно просто можно показать, что количество путей длины n от А до В равно в точности Fn. Записав матрицу смежности для графа, мы получим такую же матрицу, которая была описана выше. Это известный результат из теории графов, что при заданной матрице смежности А, вхождения в А n — это количество путей длины n в графе (одна из задач, упоминавшихся в фильме «Умница Уилл Хантинг»).
Почему на рёбрах стоят такие обозначения? Оказывается, что при рассмотрении бесконечной последовательности символов на бесконечной в обе стороны последовательности путей на графе, вы получите нечто под названием «подсдвиги конечного типа», представляющее собой тип системы символической динамики. Конкретно этот подсдвиг конечного типа известен, как «сдвиг золотого сечения», и задаётся набором «запрещённых слов» <11>. Иными словами, мы получим бесконечные в обе стороны двоичные последовательности и никакие пары из них не будут смежными. Топологическая энтропия этой динамической системы равна золотому сечению ϕ. Интересно, как это число периодически появляется в разных областях математики.
Нормально ли сделал тестовое задание на PHP (числа Фибоначчи)?
Работал почти всегда на себе, нет опыта в тестовых заданиях. Вот работодатель скинул три задания, сделал одно для интереса, попинайте.. как в общем с оформлением/алгоритмом, в таком ключе нормально будет?
Скажу сразу, алогоритм сам написал, да есть в сети тыщи вариантом, в том числе через рекурсию, но уж очень наворочено как-то.. да красиво, но не совсем понятно )
Короче вот задание (я его изменил немного):
Дан массив [3279, 920, 4181, 8, 1, 4360, 407, 9950, 2098, 8579, 4914, 7204, 8875]. В нем нужно найти числа Фибоначчи ( 1, 1, 2, 3, 5, 8, 13, 21. ), затем вычислить сумму.
Вот что получилось:
А если во входящих данных будет число на несколько тысяч знаков длинной, то ваш скрипт по таймауту отвалится, по переполнению памяти, или за пару лет справится с задачей подсчета всех промежуточных чисел?
Осталось перебрать и сложить.
Я свои пять копеек вставлю не с точки зрения алгоритма, а с точки зрения чистокододрочера:
Это что вообще за дичь? Вы на джуниор битрикс фронтендера устраиваетесь? Вы вообще про ООП слышали? Или что, если для тестового, то можно и по процедурить? Зачем тут вообще html? Это было задание? Ваш код нарушает все принципы современной разработки. Я бы кандидату с таким кодом даже не перезвонил.
Зачем тут комментарии? Вы думаете человек, который будет ревьюить код не поймет что он делает? Или вы обезьяне этот код показываете так что нужно объяснить такую строку
И вы что в нулевых остались? Почему массив создается уродливым array(), а не []?
Ну и да, алгоритм очень плохой. Можно написать короче и симпатичнее.
Нормально ли сделал тестовое задание на PHP (числа Фибоначчи)?
Работал почти всегда на себе, нет опыта в тестовых заданиях. Вот работодатель скинул три задания, сделал одно для интереса, попинайте.. как в общем с оформлением/алгоритмом, в таком ключе нормально будет?
Скажу сразу, алогоритм сам написал, да есть в сети тыщи вариантом, в том числе через рекурсию, но уж очень наворочено как-то.. да красиво, но не совсем понятно )
Короче вот задание (я его изменил немного):
Дан массив [3279, 920, 4181, 8, 1, 4360, 407, 9950, 2098, 8579, 4914, 7204, 8875]. В нем нужно найти числа Фибоначчи ( 1, 1, 2, 3, 5, 8, 13, 21. ), затем вычислить сумму.
Вот что получилось:
А если во входящих данных будет число на несколько тысяч знаков длинной, то ваш скрипт по таймауту отвалится, по переполнению памяти, или за пару лет справится с задачей подсчета всех промежуточных чисел?
Осталось перебрать и сложить.
Я свои пять копеек вставлю не с точки зрения алгоритма, а с точки зрения чистокододрочера:
Это что вообще за дичь? Вы на джуниор битрикс фронтендера устраиваетесь? Вы вообще про ООП слышали? Или что, если для тестового, то можно и по процедурить? Зачем тут вообще html? Это было задание? Ваш код нарушает все принципы современной разработки. Я бы кандидату с таким кодом даже не перезвонил.
Зачем тут комментарии? Вы думаете человек, который будет ревьюить код не поймет что он делает? Или вы обезьяне этот код показываете так что нужно объяснить такую строку
И вы что в нулевых остались? Почему массив создается уродливым array(), а не []?
Ну и да, алгоритм очень плохой. Можно написать короче и симпатичнее.