#python #python-2.7 #dictionary #fibonacci
#python #python-2.7 #словарь #фибоначчи
Вопрос:
Ладно, этот вопрос немного странный, но мне было интересно, могу ли я сделать это следующим образом.
Я работаю над простым генератором чисел Фибоначчи для развлечения, так как мне было интересно его программировать. Итак, я написал это:
def f(n):
if n == 1: return 1
if n == 2: return 2
else:
return f(n-1) f(n-2)
и это выполнялось очень медленно, на выполнение чего ушло 15 секунд f(30)
на моем компьютере.
Итак, затем я написал это:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) f(n-2)
a["fib(%s)" % n] = z
return z
который в основном сохраняет предыдущие результаты в словаре следующим образом:
{'f(1)':1,'f(2)':2,'f(3)':3,'f(4)':5}
и так далее. В функции это проверило бы, был ли этот результат в этом словаре, затем просто используйте это вместо того, чтобы переделывать все вычисления.
Это сделало его намного быстрее. Я мог бы сделать f(100)
, и он мгновенно появится. Пройдя интервалы в 500, я добрался до f(4000)
, и это все равно было мгновенно. Единственная проблема заключалась в том, что словарь становился глупо большим.
Итак, я добавил a = {}
в конец функции, и это не сработало; он по-прежнему оставался a
в виде массивного dict.
Итак, делая это:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) f(n-2)
a["fib(%s)" % n] = z
return z
a = {}
не сработало. но если я сделаю это:
def f(n):
global a
if n == 1: return 1
if n == 2: return 1
else:
if "fib(%s)" % n in a:
return a["fib(%s)" % n]
else:
z = f(n-1) f(n-2)
a["fib(%s)" % n] = z
return z
# now run the function
f(100)
a = {}
a
возвращает значение к пустому словарю. Почему это происходит и как я могу это исправить?
Комментарии:
1. Добавлено a = {} прямо перед возвратом z ?
2. Нет @Skycc, это не работает. Он сбрасывается каждый раз, когда выполняется, делая всю идею бесполезной. Что я хочу сделать, так это сбросить словарь после вызова последней рекурсии.
3. странно, это работает на моей стороне «, если «fib (%s)» % n в a:» будет вызываться рекурсивно, часть else будет последней частью рекурсии, return z уже завершит работу функции
4. Просто примечание, вы можете значительно уменьшить свой dict, сделав ключи словаря индексом Фибоначчи (n, аргумент функции) вместо строк. Также вообще нет необходимости делать это рекурсивно; что-то вроде
a, b = 0, 1; for _ in range(n): a, b = b, a b
5. Спасибо @James, я сократил словарь
Ответ №1:
Ваш a = {}
оператор внутри функции никогда не выполнялся; каждый возможный путь выполнения достигает return
до этого. Если бы это было выполнено, вам бы не понравились результаты — оно выполнялось бы при каждом отдельном рекурсивном вызове функции, а это означает, что ваш словарь никогда не содержал бы более одного элемента! Вам каким-то образом пришлось бы обнаруживать самый внешний вызов и очищать dict только там или (что намного проще) очищать его вне рекурсии, как в вашем втором примере.
Обратите внимание, что большая часть размера вашего словаря обусловлена странным решением использовать длинный строковый ключ. Ввод в него самого числа (как в a[n] = z
) сделал бы его намного более компактным.
(Для дальнейшего использования: метод, который вы здесь придумали, для сохранения результатов предыдущих вызовов функций, известен как «запоминание».)
Комментарии:
1. Спасибо! Я попробую это сжатие списка. Как я могу обнаружить самый внешний вызов?
Ответ №2:
Несмотря на ваш вопрос, то, что вы действительно хотите, это более быстрый способ вычисления последовательности Фибоначчи, верно? Проблема с вашим первоначальным подходом заключается в том, что повторение, несмотря на то, что оно очень элегантное и быстрое в написании кода, происходит довольно медленно. Последовательность Фибоначчи имеет решение близкой формы. Вы должны выполнить эту математику напрямую, чтобы ускорить свой код. В качестве соглашения рассмотрим последовательность Фибоначчи F (i) следующим образом: F(0) = 0, F(1) = 1, F(k) = F(k-1) F (k-2) k = 2, 3, … Решение для этой последовательности (я не буду демонстрировать это здесь, потому что для этого не место) F (k) = (1 / sqrt(5)) *(a ^ k — b ^ k), где a = (1 sqrt(5)) / 2 и b = (1 — sqrt(5)) / 2. Таким образом, ваш код может быть реализован следующим образом:
def f(n):
a = (1 5**.5)/2
b = (1 - 5**.5)/2
F = (a**n - b**n)/5**.5
F = int(round(F)) #necessary to get an integer without the decimal part of the approximation. Afterall, you are working with irrational numbers.
return F
Этот код очень хорошо масштабируется для больших значений n.