Решение без грубой силы для проекта Эйлера 25
последовательность Фибоначчи определяется рекуррентным отношением:
Fn = Fn-1 + Fn-2, где F1 = 1 и F2 = 1. Отсюда первые 12 терминов будет F1 = 1, F2 = 1, F3 = 2, F4 = 3, F5 = 5, F6 = 8, F7 = 13, F8 = 21, F9 = 34, F10 = 55, F11 = 89, F12 = 144
12-й член, F12, является первым термином, содержащим три цифры.
каков первый член в последовательности Фибоначчи, содержащий 1000 цифры?
Я сделал решение грубой силы в Python, но для вычисления фактического решения требуется абсолютно вечность. Может кто-нибудь подсказать номера решение грубой силы?
def Fibonacci(NthTerm):
if NthTerm == 1 or NthTerm == 2:
return 1 # Challenge defines 1st and 2nd term as == 1
else: # recursive definition of Fib term
return Fibonacci(NthTerm-1) + Fibonacci(NthTerm-2)
FirstTerm = 0 # For scope to include Term in scope of print on line 13
for Term in range(1, 1000): # Arbitrary range
FibValue = str(Fibonacci(Term)) # Convert integer to string for len()
if len(FibValue) == 1000:
FirstTerm = Term
break # Stop there
else:
continue # Go to next number
print "The first term in thenFibonacci sequence toncontain 1000 digitsnis the", FirstTerm, "term."
8 ответов
вы можете написать функцию Фибоначчи, которая работает в линейном времени и с постоянным объемом памяти, вам не нужен список, чтобы сохранить их. Вот рекурсивная версия (однако, если n достаточно большой, он будет просто stackoverflow)
def fib(a, b, n):
if n == 1:
return a
else:
return fib(a+b, a, n-1)
print fib(1, 0, 10) # prints 55
эта функция вызывает себя только один раз (в результате около N вызывает параметр N), в отличие от вашего решения, которое вызывает себя дважды (около 2^n вызывает параметр N).
вот версия, которая не будет ever stackoverflow и использует цикл вместо рекурсии:
def fib(n):
a = 1
b = 0
while n > 1:
a, b = a+b, a
n = n - 1
return a
print fib(100000)
и это достаточно быстро:
$ time python fibo.py
3364476487643178326662161200510754331030214846068006390656476...
real 0m0.869s
но вызов fib
пока вы не получите достаточно большой результат, это не идеально: первые номера серии вычисляются несколько раз.
Вы можете вычислить следующее число Фибоначчи и проверить его размер в том же цикле:
a = 1
b = 0
n = 1
while len(str(a)) != 1000:
a, b = a+b, a
n = n + 1
print "%d has 1000 digits, n = %d" % (a, n)
использовать Бине формула. Это самый быстрый способ найти числа Фибоначчи, и он не использует рекурсию.
две вещи можно оптимизировать много с одним небольшим изменением в вашем коде. Вот эти две вещи:--6-->
вы вычисляете каждое число Фибоначчи, используя два других числа Фибоначчи, что приводит к экспоненциальной сложности (которая взрывается, даже если вы вычисляете только одно, но высокое число Фибоначчи).
вы не помните никакого предыдущего вычисленного числа Фибоначчи для вычисления следующего в вашем цикле.
просто помните все вычисленные числа Фибоначчи как частную деталь реализации в Fibonacci
и вы избавляетесь от обеих проблем производительности. Вы можете сделать это с помощью простого динамического массива, в который вы добавляете результат, если он не был кэширован раньше.
псевдо-код (я не говорю на языке Python, но это может быть легко реализовано):
def Fibonacci(NthTerm):
if (cache contains NthTerm)
return cache[NthTerm]
else
FibValue = Fibonacci(NthTerm-1) + Fibonacci(NthTerm-2)
cache[NthTerm] = FibValue
return FibValue
это приведет к очень ограниченной рекурсии, так как вы вычисляете N-е число Фибоначчи, только если вы уже знаете (и кэшируете) (N-1) - е число.
эта оптимизация работает, даже если вам нужно любой число Фибоначчи (для будущих задач), но в этом конкретном случае мы знаем, что нам нужно помнить только последние два числа, так как мы никогда не будем просить старые числа снова. Поэтому вам не нужен целый список чисел, а только два, которые вы "прокручиваете" для каждого шага в своем основном цикле. Что-то вроде
f1, f2 = f2, f1 + f2
внутри цикла и инициализации как
f1, f2 = 1, 1
по существу заменит вашу функцию Fibonacci
и его проблемы с производительностью, но это ограничивает вас в этом случае использовать.
вы можете попробовать использовать метод приближения Ньютона on формула Бине. Идея состоит в том, чтобы найти касательную линию на графике и использовать X-перехват этой линии для аппроксимации значения нуля графика.
почему никто не использовал генераторы для этого? Это решение грубой силы, но оно очень быстрое:
def fibo():
a = 0
b = 1
while True:
yield b
a,b = b,a+b
это дает генератор, который вычисляет последовательность Фибоначчи. Например
f = fibo()
[next(f) for i in range(10)]
производит
[1,1,2,3,5,8,13,21,34,55]
используя это, мы можем решить проблему следующим образом:
f = enumerate(fibo())
x = 0
while len(str(x)) < 1000:
i,x = next(f)
print("The %d-th term has %d digits"%(i+1,len(str(x))))
это производит вывод
The 4782-th term has 1000 digits
генератор вычисляет последовательность и производит плане 1 на 1 и это решение работает почти немедленно.
вместо рекурсивного вычисления каждого члена каждый раз, сделать массив терминов, то вы можете вычислить термин, добавив термины[-1] и термины[-2]
вот версия Java в постоянном пространстве и линейном времени:
static int q24(){
int index = 3;
BigInteger fn_2 = new BigInteger("1");
BigInteger fn_1 = new BigInteger("1");
BigInteger fn = fn_1.add(fn_2);
while(fn.toString().length()<1000){
fn_2 = fn_1;
fn_1 = fn;
fn = fn_2.add(fn_1);
index++;
}
return index;
}
вы можете использовать запоминание :
m={}
def fub(n):
if n not in m:
if n <= 2 :
m[n] = 1
else:
m[n] = fub(n-1) + fub(n-2)
return m[n]
i=1
while len(str(fub(i))) != 1000:
i+=1
print(i)