Обратный алгоритм Фибоначчи?

Есть десятки способов вычисления F (n) для произвольного n, многие из которых имеют отличное время выполнения и использование памяти.

Однако предположим, что я хочу задать противоположный вопрос:

Учитывая F (n) для n> 2, что такое n?

(Ограничение n> 2 присутствует, поскольку F (1) = F (2) = 1 и нет однозначного обратного).

Какой способ решения этой проблемы был бы наиболее эффективным? Это легко сделать в линейном времени, перечислив числа Фибоначчи и остановившись при достижении целевого числа, но есть ли способ сделать это быстрее, чем это?

РЕДАКТИРОВАТЬ: в настоящее время лучшее решение, опубликованное здесь, выполняется за время O (log n) с использованием памяти O (log n), предполагая, что математические операции выполняются за O (1) и что машинное слово может содержать любые число в O (1) пространстве. Мне любопытно, можно ли отказаться от требований к памяти, поскольку вы можете вычислять числа Фибоначчи, используя пространство O (1).


person templatetypedef    schedule 02.03.2011    source источник
comment
Вы можете найти полезное обсуждение в вопросе, связанном с math.exchange: [check-if-a-number-is-a-fibonacci-or-not]: math.stackexchange.com/questions/9999/   -  person ypercubeᵀᴹ    schedule 03.03.2011
comment
Я мог бы назвать это логарифмом Фибоначчи   -  person President James K. Polk    schedule 04.03.2011
comment
Это очень интересная проблема, потому что она действительно спрашивает, можно ли выполнить эффективный двоичный поиск в общей группе со сравнением. То есть мы можем использовать только плюс и минус, без деления или других причудливых операций.   -  person Thomas Ahle    schedule 15.06.2014


Ответы (10)


Поскольку OP спросил о матричном решении, не включающем вычисления с плавающей запятой, вот оно. Таким образом мы можем достичь O(logn) сложности, предполагая, что числовые операции имеют O(1) сложность.

Возьмем матрицу 2x2 A, имеющую следующую структуру

1 1
1 0

Теперь рассмотрим вектор (8, 5), в котором хранятся два последовательных числа Фибоначчи. Если вы умножите его на эту матрицу, вы получите (8*1 + 5*1, 8*1 + 5*0) = (13, 8) - следующее число Фибоначчи.
Если обобщить, A^n * (1, 0) = (f(n), f(n - 1)).

Фактический алгоритм состоит из двух шагов.

  1. Вычисляйте A^2, A^4, A^8 и т. Д., Пока мы не передадим желаемое число.
  2. Выполните двоичный поиск по n, используя вычисленную степень A.

Кстати, любая последовательность формы f(n) = k1*f(n-1) + k2*f(n-2) + k3*f(n-3) + .. + kt*f(n-t) может быть представлена ​​так.

person Nikita Rybak    schedule 02.03.2011
comment
Я все еще не совсем понимаю, что именно вы делаете после того, как передадите желаемый номер. Как именно найти ответ? - person templatetypedef; 02.03.2011
comment
@templatetypedef Представьте, что мы передали f в A^16, поэтому мы выполняем двоичный поиск в диапазоне [0, 16]. mid равно 8, а мы уже A^8 вычислили. Скажем f > A^8, тогда диапазон уменьшится до [8, 16]. Сейчас mid 12, а A^12 A^8*A^4. 8 - это текущая граница поиска, а 4 - это степень двойки: поэтому мы оба вычислили и можем вычислить A^12 за одно умножение. И так далее. - person Nikita Rybak; 02.03.2011
comment
@templatetypedef Сравнение матриц с числами (f) немного упрощает, но это должно дать представление. - person Nikita Rybak; 02.03.2011
comment
@ Никита, я бы не назвал это бинарным поиском. Что нам нужно, так это разложение n по основанию 2, и затем матрицы могут быть созданы до этого момента. Возьмем 11 = 1 + 2 + 8, откуда следует, что F (11) = A ^ 11 = A * A ^ 2 * A ^ 8. Итак, нам не нужно вычислять A ^ 16. - person rcollyer; 02.03.2011
comment
@rcollyer Возможно, вы решаете противоположную задачу: мы не знаем n и поэтому не можем его разложить. Или мне что-то не хватает в твоей идее? - person Nikita Rybak; 02.03.2011
comment
@ Никита, приношу свои извинения, я полностью пропустил то, что вы делали. - person rcollyer; 02.03.2011
comment
+1 Я никогда не видел матричного метода (когда-то его представили, это очевидно). Захотелось вернуться к расширению непрерывных дробей с их помощью. - person phkahler; 02.03.2011
comment
@Nikita Rybak: это решение круто, но есть ли способ сделать это в памяти O (1)? - person templatetypedef; 03.03.2011
comment
@templatetypedef Боюсь, что нет, я ничего не могу придумать. (Мы можем переключиться на рекурсию, но это та же память O(logn), только скрытая.) С другой стороны, мы можем запоминать и повторно использовать степени A. - person Nikita Rybak; 03.03.2011
comment
Отличное решение. В любом случае, я предлагаю вам отредактировать свой ответ, включив в него комментарий, сделанный в 4:02 2 марта. - person Haozhun; 10.03.2011
comment
Похоже, вы можете сделать это в памяти O (1), используя трюк Цекендорфа ниже. Учитывая это, это действительно отличный ответ. Большое спасибо! - person templatetypedef; 12.03.2011

Википедия дает результат как

n(F) = Floor[ Log(F Sqrt(5) + 1/2)/Log(Phi)]

где Phi - золотое сечение.

person rcollyer    schedule 02.03.2011
comment
Как быстро можно вычислить этот логарифм? А с каким использованием памяти? - person templatetypedef; 02.03.2011
comment
(Я спрашиваю, потому что, если это все еще занимает линейное время, это не намного лучше, чем наивное решение) - person templatetypedef; 02.03.2011
comment
Этот n(F) - самый быстрый способ вычислить n для данного F(n) (игнорируя тот факт, что n(1) возвращает 2). Однако он не гарантирует, что F на самом деле является членом последовательности Фибоначчи (при большом F числа около F дадут тот же результат). - person Michelle Tilley; 02.03.2011
comment
Хорошая точка зрения. Но журнал можно разделить на два члена log(F) + log(5)/2, и оба log(5) и log(phi) могут быть вычислены заранее. Используйте базу 5, и хоть отчасти это даже проще. Таким образом, всякий раз, когда вы хотите получить ответ, нужно вычислять только сам log(F). - person rcollyer; 02.03.2011
comment
Это можно вычислить за постоянное время, так как почти на каждом языке есть функции, которые выполняют log и sqrt за постоянное время. - person Dan; 02.03.2011
comment
@Brandon: нет, но быстрое вычисление Fib(n) с n, которое вы нашли с помощью n (F), и сравнение этого с исходным F дает понять, было ли это выдумкой или нет. - person Dan; 02.03.2011
comment
@Dan Мне это показалось интересным: вы также можете проверить, пересекаются ли phi * n - (1.0 / n) и phi * n + (1.0 / n) положительное целое число. Например. для n = 144 вы получите 232.9899 и 233.0038, что пересекает 233. Использование того же вычисления для n = 143 дает 231.3718 и 231.3858, а значит, не является числом Фибоначчи. - person Michelle Tilley; 02.03.2011
comment
@Dan: это постоянное время только, если вы рассматриваете числа с фиксированной верхней границей. - person R. Martinho Fernandes; 02.03.2011
comment
(продолжение) и может быть вычислено программно с помощью логического оператора floor(phi * n - 1.0 / n) != floor(phi * n + 1.0 / n) - person Michelle Tilley; 02.03.2011
comment
@ Dan: Я скептически отношусь к тому, что вы можете вести журнал за постоянное время, если не ограничиваете точность ваших чисел. Это было бы практически хорошим решением, но меня больше интересует что-то, что масштабируется как можно лучше, учитывая только базовые математические операции в качестве примитивов. - person templatetypedef; 02.03.2011
comment
@templatetypedef @Martinho: вы правы, я предполагал обычные типы данных. - person Dan; 02.03.2011
comment
Вы не можете ничего сделать за постоянное время с неограниченными числами (даже сложение требует O (log (N)). Если гипотетическая система больших чисел сохранила длину числа, то это хорошее первое приближение к логарифмической базе 2 и вы можете читать биты только с необходимой точностью. - person phkahler; 02.03.2011
comment
@templatetypedef: вы можете вычислить log_phi (F) с логарифмической функцией времени от F. Просто используйте тот же прием повторного возведения в квадрат, который мы используем при возведении в степень (en.wikipedia.org/wiki/Exponentiation_by_squaring). Итак, в рамках вашей модели вычислений это решение дает метод постоянного пространства с логарифмом времени. - person mhum; 09.03.2011
comment
@ mhum - я думаю, что этот подход требует памяти O (lg n), поскольку, если ваш двоичный поиск когда-либо превысит указанное число, вам каким-то образом понадобится сузить поиск. Я могу видеть, как получить по одному биту за раз, используя этот подход в общей сложности O (lg ^ 2 n) времени, и не видя более строгого алгоритма для выполнения этого за O (lg n) с постоянным пространством, я не уверен, что ты сможешь это сделать. Ответ user635541 ниже описывает один подход для вычисления журнала во времени O (lg n) и пространстве O (1) с использованием чисел Фибоначчи (!!), поэтому это действительно кажется возможным. - person templatetypedef; 09.03.2011
comment
@templatetypedef: На самом деле это даже проще, чем я думал, нам даже не нужно что-то вроде повторного возведения в квадрат или бинарного поиска. Чтобы вычислить, скажем, log_phi (n), начните с 1, продолжайте умножать на phi, пока не превысите n. Число умножений равно ceil (log_phi (n)), а значит, O (lg (n)). - person mhum; 09.03.2011
comment
@ mhum- Я думаю, что это выполняется за O (n), поскольку log phi ^ n = n. Помните, что n - это индекс числа Фибоначчи, а не само число Фибоначчи. - person templatetypedef; 09.03.2011
comment
@templatetypedef: Ах, я неправильно понял. В этом случае нам нужен двоичный поиск. Получим ли мы возведение в степень за постоянное время? Если это так, то мы вычисляем phi, phi ^ 2, phi ^ 4, phi ^ 16, ... до phi ^ {2 ^ k} ‹F‹ phi ^ {2 ^ {k + 1}}. Отсюда мы выполняем двоичный поиск i между 2 ^ k и 2 ^ k + 1, так что phi ^ i ‹F‹ phi ^ {i + 1}. Поскольку F (n) прибл. phi ^ n, первая часть занимает O (log (n)) шагов, а вторая также занимает O (log (n)). - person mhum; 10.03.2011
comment
@ mhum- Нет, возведение в степень не бесплатно. Предположим, что стандартные кольцевые операции (сложение, вычитание, умножение, деление) имеют удельную стоимость. На самом деле я просто закодировал отличную идею пользователя 635541 использовать числа Фибоначчи для журнала, что работает как шарм. - person templatetypedef; 10.03.2011
comment
@rcollyer: Я пробовал это с большим количеством Фибоначчи. Он немедленно возвращается для n <= 1000, но позже у меня возникнет проблема с переполнением. - person Thomas Ahle; 15.06.2014
comment
Я попытался получить это уравнение, используя альфа-вольфрам, вот так: wolfram alpha Что я делаю не так? - person gkucmierz; 06.11.2019

Если вы можете легко интерпретировать F (n) в двоичном формате,

формула

Вы можете с подозрением относиться к константам 1.7 и 1.1. Это работает, потому что d * 1.44042009041 + C никогда не приближается к целому числу.

Я могу опубликовать вывод завтра, если будет интерес.

Вот таблица с n = от 2 до 91, в которой показан результат формулы до покрытия пола:

 n  formula w/o floor     F(n) F(n) in binary

 2  2.540                    1 1
 3  3.981                    2 10
 4  4.581                    3 11
 5  5.421                    5 101
 6  6.862                    8 1000
 7  7.462                   13 1101
 8  8.302                   21 10101
 9  9.743                   34 100010
10 10.343                   55 110111
11 11.183                   89 1011001
12 12.623                  144 10010000
13 13.223                  233 11101001
14 14.064                  377 101111001
15 15.504                  610 1001100010
16 16.104                  987 1111011011
17 17.545                 1597 11000111101
18 18.385                 2584 101000011000
19 19.825                 4181 1000001010101
20 20.425                 6765 1101001101101
21 21.266                10946 10101011000010
22 22.706                17711 100010100101111
23 23.306                28657 110111111110001
24 24.147                46368 1011010100100000
25 25.587                75025 10010010100010001
26 26.187               121393 11101101000110001
27 27.028               196418 101111111101000010
28 28.468               317811 1001101100101110011
29 29.068               514229 1111101100010110101
30 30.508               832040 11001011001000101000
31 31.349              1346269 101001000101011011101
32 32.789              2178309 1000010011110100000101
33 33.389              3524578 1101011100011111100010
34 34.230              5702887 10101110000010011100111
35 35.670              9227465 100011001100110011001001
36 36.270             14930352 111000111101000110110000
37 37.111             24157817 1011100001001111001111001
38 38.551             39088169 10010101000111000000101001
39 39.151             63245986 11110001010000111010100010
40 40.591            102334155 110000110010111111011001011
41 41.432            165580141 1001110111101000110101101101
42 42.032            267914296 1111111110000000110000111000
43 43.472            433494437 11001110101101001100110100101
44 44.313            701408733 101001110011101010010111011101
45 45.753           1134903170 1000011101001010011111110000010
46 46.353           1836311903 1101101011100111110010101011111
47 47.193           2971215073 10110001000110010010010011100001
48 48.634           4807526976 100011110100011010000101001000000
49 49.234           7778742049 111001111101001100010111100100001
50 50.074          12586269025 1011101110001100110011100101100001
51 51.515          20365011074 10010111101110110010110100010000010
52 52.115          32951280099 11110101100000011001010000111100011
53 53.555          53316291173 110001101001111001100000101001100101
54 54.396          86267571272 1010000010101111100101010110001001000
55 55.836         139583862445 10000001111111110110001011011010101101
56 56.436         225851433717 11010010010101110010110110001011110101
57 57.276         365435296162 101010100010101101001000001100110100010
58 58.717         591286729879 1000100110101011011011110111110010010111
59 59.317         956722026041 1101111011000001000100111001011000111001
60 60.157        1548008755920 10110100001101100100000110001001011010000
61 61.598        2504730781961 100100011100101101100101101010100100001001
62 62.198        4052739537881 111010111110011010000110011011101111011001
63 63.038        6557470319842 1011111011011000111101100000110010011100010
64 64.478       10610209857723 10011010011001100001110010100010000010111011
65 65.078       17167680177565 11111001110100101001011110101000010110011101
66 66.519       27777890035288 110010100001110001011010001001010011001011000
67 67.359       44945570212853 1010001110000010110100101111110010101111110101
68 68.800       72723460248141 10000100010010001000000000000111101001001001101
69 69.400      117669030460994 11010110000010011110100110000101111111001000010
70 70.240      190392490709135 101011010010100100110100110001101101000010001111
71 71.681      308061521170129 1000110000010111000101001100010011100111011010001
72 72.281      498454011879264 1110001010101011101011110010100001001111101100000
73 73.121      806515533049393 10110111011000010110000111110110100110111000110001
74 74.561     1304969544928657 100101000101101110011100110001010110000110110010001
75 75.161     2111485077978050 111100000000110001001101110000001010111101111000010
76 76.602     3416454622906707 1100001000110011111101010100001100001000100101010011
77 77.442     5527939700884757 10011101000111010000111000010001101100000010100010101
78 78.042     8944394323791464 11111110001101110000100010110011001101000111001101000
79 79.483    14472334024676221 110011011010101000001011011000100111001001001101111101
80 80.323    23416728348467685 1010011001100010110001111101111000000110010000111100101
81 81.764    37889062373143906 10000110100110111110011011000111100111111011010101100010
82 82.364    61305790721611591 11011001110011010100101010110110101000101101011101000111
83 83.204    99194853094755497 101100000011010010011000101111110010000101000110010101001
84 84.644   160500643816367088 1000111010001101100111110000110100111001010110001111110000
85 85.244   259695496911122585 1110011010100111111010110110110011001001111111000010011001
86 86.085   420196140727489673 10111010100110101100010100111101000000011010101010010001001
87 87.525   679891637638612258 100101101111011101011101011110011011001101010100010100100010
88 88.125  1100087778366101931 111101000100010011000000000110000011010000101001100110101011
89 89.566  1779979416004714189 1100010110011110000011101100100011110011101111101111011001101
90 90.406  2880067194370816120 10011111111000000011011101101010100001101110100111100001111000
91 91.846  4660046610375530309 100000010101011110011111011001111000000001100100101011101000101

'

person Tom Sirgedas    schedule 10.03.2011
comment
Этот ответ - O (1) и является абсолютным триумфом - ответ @rcollyer сводится к очень гладкому вычислению. Учитывая исходные ограничения проблемы (зная, что входными данными, безусловно, является Фибоначчи), конечно, это не может быть побеждено. - person Chris Nash; 12.03.2011
comment
Я не знаю, почему вы потрудились записать приближение 1 / log_2 (phi), поскольку вам нужно lg d + O (1) бит точности. Это определенно не O (1). - person userOVER9000; 12.03.2011
comment
@ userOVER9000 Итак, получение более точного двойного приближения было бы достаточно, чтобы ответить на вопрос для ввода длиной 2 ^ 53 бита? 1 пебибайт? - person Chris Nash; 12.03.2011
comment
Это кажется опасно близким к ошибке 91. Вы также запускали его для 92? - person Thomas Ahle; 15.06.2014
comment
Нет, но мы можем это подсчитать. F (92) = F (91) + F (90). Глядя на двоичную форму F (91) и F (90), мы можем сказать, что их сумма будет иметь то же количество цифр, что и F (91), но начинаться с 11. Таким образом, формула без пола для F (92) будет будет ровно на 0,6 больше, чем для F (91), что дает ~ 92,446. - person Tom Sirgedas; 24.06.2014

Измерять использование памяти путем подсчета неограниченных слов довольно глупо, но пока это модель, существует решение O (log n), O (1) word, подобное решению Никиты Рыбака, которое, по сути, вычисляет n через его представление Цекендорфа, основанное на числах Фибоначчи (YO DAWG).

Определить матрицу

      1  1
A  =       ,
      1  0

что удовлетворяет

        F(m + 1)    F(m)
A^m  =                      .
          F(m)    F(m - 1)

Вместо последовательности A^(2^k) мы будем использовать последовательность A^F(k). Последняя последовательность обладает тем свойством, что мы можем двигаться вперед с матричным умножением

A^F(k + 1) = A^F(k - 1) * A^F(k)

и обратно с обратной матрицей и умножением

A^F(k - 1) = A^F(k + 1) (A^F(k))^-1,

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

def zeck(n):
    a, b = (0, 1)
    while b < n:
        a, b = (b, a + b)
    yield a
    n1 = a
    while n1 < n:
        a, b = (b - a, a)
        if n1 + a <= n:
            yield a
            n1 += a
            a, b = (b - a, a)

>>> list(zeck(0))
[0]
>>> list(zeck(2))
[1, 1]
>>> list(zeck(12))
[8, 3, 1]
>>> list(zeck(750))
[610, 89, 34, 13, 3, 1]
person user635541    schedule 06.03.2011
comment
Отсюда очевидно, что базовое уравнение Фибоначчи F (m + 1) = F (m-1) + F (m) является логарифмом, базирующимся на матрице A, матрицы умножения eqn A ^ F (m + 1) = A ^ F (м) * A ^ F (м-1). Сладкий математический ответ! - person Reb.Cabin; 29.02.2012
comment
Я не уверен, что понимаю, как это работает. Если вы создаете представление Цекендорфа, разве вы не тратите logn память? Вы тоже составляете список всех A^F(n) способностей? - person Thomas Ahle; 15.06.2014
comment
@ThomasAhle (этот ответ старый, но) Как указано в ответе, одновременно хранятся только два A ^ F (n). - person user202729; 22.09.2020

Доказано, что формула выдумки n - это fib(n) = ( (phi)^n - (-phi)^(-n) ) / sqrt(5), где phi = (1+sqrt(5)) / 2, число золотого сечения. (см. эту ссылку).

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

Изменить: решение @ rcollyer быстрее, так как мое находится в O (lg n), а тот, который он нашел, находится в O (1) = постоянное время.

person Dan    schedule 02.03.2011

Итак, я думал об этой проблеме и думаю, что это можно сделать за O (lg n) раз с использованием памяти O (lg n). Это основано на том, что

F (n) = (1/5) (n - n)

Где = (1 + 5) / 2 и = 1 -.

Первое наблюдение: n ‹1 для любого n> 1. Это означает, что для любого n> 2 мы имеем

F (n) = n / 5

Теперь возьмите n и запишите его в двоичном формате как b k-1 b k-2 ... b 1 b 0 < / sub>. Это означает, что

n = 2 k-1 b k-1 + 2 k-2 b k-2 + .. . + 2 1 b 1 + 2 0 b 0.

Это означает, что

F (n) = 2 k-1 b k-1 + 2 k-2 b k-2 < / sub> + ... + 2 1 b 1 + 2 0 b 0 / 5

Или, что более понятно, что

F (n) = 2 k-1 b k-1 2 k-2 b < sub> k-2 ... 2 1 b 1 2 0 < / sup> b 0 / 5

Это предполагает следующий алгоритм. Сначала начните вычислять 2 k для всех k, пока вы не вычислите число z такое, что z / 5 больше чем ваше число F (n). Теперь, оттуда, перебирайте в обратном порядке все ваши силы, сгенерированные таким образом. Если текущее число больше указанной степени, то разделите его на эту степень и запишите, что число было разделено на это значение. Этот процесс, по сути, восстанавливает один бит n за раз, вычитая наибольшую степень двойки, которую вы можете за раз. Следовательно, как только вы закончите, вы найдете n.

Время выполнения этого алгоритма - O (lg n), поскольку вы можете сгенерировать 2 i путем повторного возведения в квадрат, а мы генерируем только O (lg n) членов. Использование памяти - O (lg n), поскольку мы храним все эти значения.

person templatetypedef    schedule 02.03.2011
comment
Вы можете избежать вычислений с плавающей запятой, если вместо этого используете матрицы 2x2. Это должно быть быстрее и проще. - person Nikita Rybak; 02.03.2011
comment
Не согласен с этим. Вычислить phi ^ 2 ^ k само по себе является проблемой. Насколько точно? Затем нужно брать этажи и т. Д. Что плохого в простом бинарном поиске, вычислении Фибоначчи с использованием умножения матриц? :-П - person ; 02.03.2011
comment
@Moron, @Nikita Rybak - Мне нравится идея использовать матричное представление. Однако я не вижу, как восстановить отдельные биты из этих представлений. Не могли бы вы пояснить этот шаг? Я определенно хотел бы что-нибудь стабильное в числовом отношении! - person templatetypedef; 02.03.2011
comment
@templatetypedef Я опубликовал описание в отдельном ответе. - person Nikita Rybak; 02.03.2011
comment
@Moron Решение, основанное на умножении матриц, будет иметь те же проблемы, что и n. Только здесь нам нужно много знаков после десятичной точки, а при матричном умножении нужны огромные числа. - person Nikita Rybak; 02.03.2011
comment
@Nikita: Если вы способны обрабатывать F (n), я не понимаю, как возникнут проблемы ... - person ; 02.03.2011
comment
@Moron Не совсем так. Если f (n) умещается в int, то мы, вероятно, можем использовать эту формулу с точностью double. А если этого не произойдет, нам придется заплатить за длинную арифметику в обоих решениях, чтобы они заработали. Мне больше нравится «точная» версия с матрицами, просто говорю, что вряд ли разницы в асимптотической сложности. - person Nikita Rybak; 02.03.2011
comment
@Nikita: ИМО, было бы сложно доказать правильность алгоритмов, использующих операции с плавающей запятой: «длинная арифметика» может оказаться слишком длинной по сравнению с версией целочисленной арифметики. Конечно, степени фи, вероятно, могут быть вычислены в разумной степени путем вычисления приближений непрерывной дроби, в которых, как я полагаю, участвует Фибоначчи! Поищите в Интернете задачу о сумме квадратных корней, чтобы узнать о трудностях выбора правильной точности ... - person ; 02.03.2011

Вы можете найти n для любого Fib (n) за время O (1) и пространство O (1).

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

Если x = Fib (n), то n можно определить по формуле

     n = int(2.0801 * ln(x) + 2.1408)

Время работы CORDIC определяется желаемым уровнем точности. Два значения с плавающей запятой будут закодированы как значения с фиксированной запятой.

Единственная проблема с этим предложением заключается в том, что оно возвращает значение для чисел, которые не входят в последовательность Фибоначчи, но в исходной проблеме конкретно указано, что входом в функцию будет Fib (n), что означает, что будут использоваться только действительные числа Фибоначчи. использовал.

person oosterwal    schedule 10.03.2011

РЕДАКТИРОВАТЬ: Неважно. Автор вопроса заявил в комментариях, что возведение в степень определенно не является постоянным временем.


Является ли возведение в степень одной из математических операций, которые вы разрешите в постоянное время? Если это так, мы можем вычислить F (n) за постоянное время с помощью формулы замкнутой формы . Затем, учитывая некоторый F, мы можем сделать следующее:

  1. Вычислить F (1), F (2), F (4), F (16), F (256), ... до тех пор, пока F (2 ^ k) ‹= F‹ F (2 ^ {k + 1})
  2. Выполните двоичный поиск i между 2 ^ k и 2 ^ {k + 1}, пока F (i) ‹= F‹ F (i + 1)

Если F = F (n), то первая часть занимает k = O (log (n)) шагов. Вторая часть - это двоичный поиск в диапазоне размера O (2 ^ k), поэтому также требуется k = O (log (n)). Итак, всего у нас есть время O (log (n)) в пространстве O (1) if (и это большое если), у нас есть возведение в степень за время O (1).

person mhum    schedule 10.03.2011

Замкнутая форма формулы числа Фибоначчи:

Fn = Round(φ^n / Sqrt(5))

Где φ - золотое сечение.

Если мы проигнорируем коэффициент округления, это обратимо, и обратная функция будет:

F(-1)n= log(n*Sqrt(5))/logφ 

Поскольку мы проигнорировали коэффициент округления, в формуле есть ошибка, которую можно было бы вычислить. Однако, если мы считаем, что число n является числом Фибоначчи тогда и только тогда, когда интервал [n * φ - 1 / n, n * φ + 1 / n] содержит натуральное число, тогда:

Число является числом Фибоначчи тогда и только тогда, когда интервал [n * φ - 1 / n, n * φ + 1 / n] содержит натуральное число, а индекс этого числа в последовательности Фибоначчи задается округлением log (n * Sqrt (5) ) / logφ

Это должно быть выполнено за (псевдо) постоянное время в зависимости от алгоритмов, используемых для вычисления логарифма, квадратного корня и т. Д.

Изменить: φ = (1 + Sqrt (5)) / 2

person apokryfos    schedule 09.09.2011

Это может быть похоже на ответ user635541. Я не совсем понимаю его подход.

Используя матричное представление чисел Фибоначчи, обсуждаемое в других ответах, мы получаем способ перейти от F_n и F_m к F_{n+m} и F_{n-m} в постоянное время, используя только плюс, умножение, минус и деление (на самом деле нет! См. Обновление ). У нас также есть ноль (единичная матрица), так что это математическая группа!

Обычно при бинарном поиске нам также нужен оператор деления для взятия средних значений. Или, по крайней мере, делением на 2. Однако, если мы хотим перейти от F_{2n} к F_n, потребуется квадратный корень. К счастью, оказывается, что плюс и минус - это все, что нам нужно для «почти» двоичного поиска по логарифмическому времени!

Википедия пишет о подходе, который иронично называется Fibonacci_search, но статья написана не очень четко, поэтому я не знаю, точно ли это такой же подход, как у меня. Очень важно понимать, что числа Фибоначчи, используемые для поиска Фибоначчи, не имеют ничего общего с числами, которые мы ищем. Это немного сбивает с толку. Чтобы продемонстрировать этот подход, сначала приведем реализацию стандартного «двоичного поиска» только с использованием плюса и минуса:

def search0(test):
    # Standard binary search invariants:
    #   i <= lo then test(i)
    #   i >= hi then not test(i)
    # Extra invariants:
    #   hi - lo = b
    #   a, b = F_{k-1}, F_k
    a, b = 0, 1
    lo, hi = 0, 1
    while test(hi):
        a, b = b, a + b
        hi = b
    while b != 1:
        mi = lo + a
        if test(mi):
            lo = mi
            a, b = 2*a - b, b - a
        else:
            hi = mi
            a, b = b - a, a
    return lo

>>> search0(lambda n: n**2 <= 25)
5
>>> search0(lambda n: 2**n <= 256)
8

Здесь test - некоторая логическая функция; a и b - это последовательные числа Фибоначчи f_k и f_{k-1}, так что разница между верхней границей hi и нижней границей lo всегда равна f_k. Нам нужны как a, так и b, чтобы мы могли эффективно увеличивать и уменьшать неявную переменную k.

Хорошо, так как мы можем использовать это для решения проблемы? Я счел полезным создать оболочку вокруг нашего представления Фибоначчи, которая скрывает детали матрицы. На практике (есть ли такая вещь для искателя Фибоначчи?) вы бы хотели встроить все вручную. Это избавит вас от избыточности в матрицах и сделает некоторую оптимизацию вокруг инверсии матриц.

import numpy as np
class Fib:
    def __init__(self, k, M):
        """ `k` is the 'name' of the fib, e.g. k=6 for F_6=8.
             We need this to report our result in the very end.
            `M` is the matrix representation, that is
             [[F_{k+1}, F_k], [F_k, F_{k-1}]] """
        self.k = k
        self.M = M
    def __add__(self, other):
        return Fib(self.k + other.k, self.M.dot(other.M))
    def __sub__(self, other):
        return self + (-other)
    def __neg__(self):
        return Fib(-self.k, np.round(np.linalg.inv(self.M)).astype(int))
    def __eq__(self, other):
        return self.k == other.k
    def value(self):
        return self.M[0,1]

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

def search(test):
    Z = Fib(0, np.array([[1,0],[0,1]])) # Our 0 element
    A = Fib(1, np.array([[1,1],[1,0]])) # Our 1 element
    a, b = Z, A
    lo, hi = Z, A
    while test(hi.value()):
        a, b = b, a + b
        hi = b
    while b != A:
        mi = lo + a
        if test(mi.value()):
            lo = mi
            a, b = a+a-b, b-a
        else:
            hi = mi
            a, b = b-a, a
    return lo.k

>>> search(lambda n: n <= 144)
12
>>> search(lambda n: n <= 0)
0

Остается открытым вопрос: существует ли эффективный алгоритм поиска моноидов. Это тот, который не нуждается в обратном минусе / добавлении. Думаю, нет: без минуса нужна дополнительная память о Никите Рыбаке.

Обновлять

Я просто понял, что деление нам вообще не нужно. Определитель матрицы F_n равен просто (-1)^n, так что мы действительно можем делать все без деления. Ниже я удалил весь матричный код, но сохранил класс Fib только потому, что в противном случае все стало очень запутанным.

class Fib2:
    def __init__(self, k, fp, f):
        """ `fp` and `f` are F_{k-1} and F_{k} """
        self.k, self.fp, self.f = k, fp, f
    def __add__(self, other):
        fnp, fn, fmp, fm = self.fp, self.f, other.fp, other.f
        return Fib2(self.k + other.k, fn*fm+fnp*fmp, (fn+fnp)*fm+fn*fmp)
    def __sub__(self, other):
        return self + (-other)
    def __neg__(self):
        fp, f = self.f + self.fp, -self.f
        return Fib2(-self.k, (-1)**self.k*fp, (-1)**self.k*f)
    def __eq__(self, other):
        return self.k == other.k
    def value(self):
        return self.f

def search2(test):
    Z = Fib2(0, 1, 0)
    A = Fib2(1, 0, 1)
    ...

>>> search2(lambda n: n <= 280571172992510140037611932413038677189525)
200
>>> search2(lambda n: n <= 4224696333392304878706725602341482782579852840250681098010280137314308584370130707224123599639141511088446087538909603607640194711643596029271983312598737326253555802606991585915229492453904998722256795316982874482472992263901833716778060607011615497886719879858311468870876264597369086722884023654422295243347964480139515349562972087652656069529806499841977448720155612802665404554171717881930324025204312082516817125)
2000
>>> search2(lambda n: n <= 2531162323732361242240155003520607291766356485802485278951929841991312781760541315230153423463758831637443488219211037689033673531462742885329724071555187618026931630449193158922771331642302030331971098689235780843478258502779200293635651897483309686042860996364443514558772156043691404155819572984971754278513112487985892718229593329483578531419148805380281624260900362993556916638613939977074685016188258584312329139526393558096840812970422952418558991855772306882442574855589237165219912238201311184749075137322987656049866305366913734924425822681338966507463855180236283582409861199212323835947891143765414913345008456022009455704210891637791911265475167769704477334859109822590053774932978465651023851447920601310106288957894301592502061560528131203072778677491443420921822590709910448617329156135355464620891788459566081572824889514296350670950824208245170667601726417091127999999941149913010424532046881958285409468463211897582215075436515584016297874572183907949257286261608612401379639484713101138120404671732190451327881433201025184027541696124114463488665359385870910331476156665889459832092710304159637019707297988417848767011085425271875588008671422491434005115288334343837778792282383576736341414410248994081564830202363820504190074504566612515965134665683289356188727549463732830075811851574961558669278847363279870595320099844676879457196432535973357128305390290471349480258751812890314779723508104229525161740643984423978659638233074463100366500571977234508464710078102581304823235436518145074482824812996511614161933313389889630935320139507075992100561077534028207257574257706278201308302642634678112591091843082665721697117838726431766741158743554298864560993255547608496686850185804659790217122426535133253371422250684486113457341827911625517128815447325958547912113242367201990672230681308819195941016156001961954700241576553750737681552256845421159386858399433450045903975167084252876848848085910156941603293424067793097271128806817514906531652407763118308162377033463203514657531210413149191213595455280387631030665594589183601575340027172997222489081631144728873621805528648768511368948639522975539046995395707688938978847084621586473529546678958226255042389998718141303055036060772003887773038422366913820397748550793178167220193346017430024134496141145991896227741842515718997898627269918236920453493946658273870473264523119133765447653295022886429174942653014656521909469613184983671431465934965489425515981067546087342348350724207583544436107294087637975025147846254526938442435644928231027868701394819091132912397475713787593612758364812687556725146456646878912169274219209708166678668152184941578590201953144030519381922273252666652671717526318606676754556170379350956342095455612780202199922615392785572481747913435560866995432578680971243966868110016581395696310922519803685837460795358384618017215468122880442252343684547233668502313239328352671318130604247460452134121833305284398726438573787798499612760939462427922917659263046333084007208056631996856315539698234022953452211505675629153637867252695056925345220084020071611220575700841268302638995272842160994219632684575364180160991884885091858259996299627148614456696661412745040519981575543804847463997422326563897043803732970397488471644906183310144691243649149542394691524972023935190633672827306116525712882959108434211652465621144702015336657459532134026915214509960877430595844287585350290234547564574848753110281101545931547225811763441710217452979668178025286460158324658852904105792472468108996135476637212057508192176910900422826969523438985332067597093454021924077101784215936539638808624420121459718286059401823614213214326004270471752802725625810953787713898846144256909835116371235019527013180204030167601567064268573820697948868982630904164685161783088076506964317303709708574052747204405282785965604677674192569851918643651835755242670293612851920696732320545562286110332140065912751551110134916256237884844001366366654055079721985816714803952429301558096968202261698837096090377863017797020488044826628817462866854321356787305635653577619877987998113667928954840972022833505708587561902023411398915823487627297968947621416912816367516125096563705174220460639857683971213093125)
20000

Все это работает как шарм. Меня беспокоит только то, что битовая сложность настолько доминирует в вычислениях, что мы могли бы просто выполнить последовательный поиск. Или на самом деле, просто глядя на количество цифр, вы, вероятно, можете многое сказать, на что вы смотрите. Хотя это не так весело.

person Thomas Ahle    schedule 14.06.2014