Создать более быструю функцию Фибоначчи для n> 100 в MATLAB/октаву
У меня есть функция, которая сообщает мне n-ое число в последовательности Фибоначчи. Проблема в том, что при попытке найти большее число в последовательности Фибоначчи это становится очень медленным, кто-нибудь знает, как я могу это исправить?
function f = rtfib(n)
if (n==1)
f= 1;
elseif (n == 2)
f = 2;
else
f =rtfib(n-1) + rtfib(n-2);
end
Результаты,
tic; rtfib(20), toc
ans = 10946
Elapsed time is 0.134947 seconds.
tic; rtfib(30), toc
ans = 1346269
Elapsed time is 16.6724 seconds.
Я не могу даже получить значение через 5 минут, сделав rtfib(100)
PS: Я использую октаву 3.8.1
Ответы
Ответ 1
Если важно время (а не методы программирования):
function f = fib(n)
if (n == 1)
f = 1;
elseif (n == 2)
f = 2;
else
fOld = 2;
fOlder = 1;
for i = 3 : n
f = fOld + fOlder;
fOlder = fOld;
fOld = f;
end
end
end
tic;fib(40);toc; ans = 165580141; Elapsed time is 0.000086 seconds.
Вы даже можете использовать uint64
. n = 92
- это самое большее, что вы можете получить от uint64
:
tic;fib(92);toc; ans = 12200160415121876738; Elapsed time is 0.001409 seconds.
Поскольку,
fib(93) = 19740274219868223167 > intmax('uint64') = 18446744073709551615
Изменить
Чтобы получить fib(n)
до n = 183
, можно использовать два uint64 как одно число,
со специальной функцией для суммирования,
function [] = fib(n)
fL = uint64(0);
fH = uint64(0);
MaxNum = uint64(1e19);
if (n == 1)
fL = 1;
elseif (n == 2)
fL = 2;
else
fOldH = uint64(0);
fOlderH = uint64(0);
fOldL = uint64(2);
fOlderL = uint64(1);
for i = 3 : n
[fL q] = LongSum (fOldL , fOlderL , MaxNum);
fH = fOldH + fOlderH + q;
fOlderL = fOldL;
fOlderH = fOldH;
fOldL = fL;
fOldH = fH;
end
end
sprintf('%u',fH,fL)
end
LongSum
:
function [s q] = LongSum (a, b, MaxNum)
if a + b >= MaxNum
q = 1;
if a >= MaxNum
s = a - MaxNum;
s = s + b;
elseif b >= MaxNum
s = b - MaxNum;
s = s + a;
else
s = MaxNum - a;
s = b - s;
end
else
q = 0;
s = a + b;
end
Примечание некоторые осложнения в LongSum
могут показаться ненужными, но это не так!
(Вся сделка с внутренним if
заключается в том, что я хотел избежать s = a + b - MaxNum
в одной команде, потому что он может переполнять и хранить нерелевантное число в s
)
Результаты
tic;fib(159);toc; Elapsed time is 0.009631 seconds.
ans = 1226132595394188293000174702095995
tic;fib(183);toc;
Истекшее время - 0,009735 секунд.
fib (183) = 127127879743834334146972278486287885163
Однако вы должны быть осторожны в отношении sprintf
.
Я также сделал это с тремя uint64, и я мог бы подняться,
tic;fib(274);toc;
Истекшее время - 0.032249 секунд.
ans = 1324695516964754142521850507284930515811378128425638237225
(Это почти тот же код, но я мог бы поделиться им, если вам интересно).
Примечание, что у нас есть fib(1) = 1 , fib(2) = 2
в соответствии с вопросом, в то время как это чаще встречается с fib(1) = 1 , fib(2) = 1
, первые 300 фибов перечислены здесь (спасибо @Rick T).
Ответ 2
Похоже, что ряд фибоначчи следует за golden ratio
, о чем подробно говорится here
.
Это было использовано в этот код обмена файлами MATLAB, и я пишу здесь, просто из-за этого -
sqrt5 = sqrt(5);
alpha = (1 + sqrt5)/2; %// alpha = 1.618... is the golden ratio
fibs = round( alpha.^n ./ sqrt5 )
Вы можете передать целое число в n
для номера nth
в Fibonacci Series
или передать массив 1:n
для всей серии.
Обратите внимание, что этот метод сохраняется до n = 69
.
Ответ 3
Если у вас есть доступ к Symbolic Math Toolbox в MATLAB, вы всегда можете просто call функция Fibonacci из MuPAD:
>> fib = @(n) evalin(symengine, ['numlib::fibonacci(' num2str(n) ')'])
>> fib(274)
ans =
818706854228831001753880637535093596811413714795418360007
Это довольно быстро:
>> timeit(@() fib(274))
ans =
0.0011
Плюс вы можете пойти на столько больших, сколько хотите (ограничено только тем, сколько у вас RAM!), оно все еще быстро вспыхивает:
% see if you can beat that!
>> tic
>> x = fib(100000);
>> toc % Elapsed time is 0.004621 seconds.
% result has more than 20 thousand digits!
>> length(char(x)) % 20899
Вот полное значение fib(100000)
: http://pastebin.com/f6KPGKBg
Ответ 4
Чтобы достичь больших чисел, вы можете использовать символическое вычисление. В Matlab R2010b работает следующее.
syms x y %// declare variables
z = x + y; %// define formula
xval = '0'; %// initiallize x, y values
yval = '1';
for n = 2:300
zval = subs(z, [x y], {xval yval}); %// update z value
disp(['Iteration ' num2str(n) ':'])
disp(zval)
xval = yval; %// shift values
yval = zval;
end
Ответ 5
Одна проблема с производительностью заключается в том, что вы используете рекурсивное решение. Итеративный метод избавит вас от аргумента, передаваемого для каждого вызова функции. Как отметил Оливье, это уменьшит сложность до линейного.
Вы также можете посмотреть здесь. По-видимому, существует формула, которая вычисляет n-й член последовательности Фибоначчи. Я тестировал его до 50-го элемента. Для более высоких значений n это не очень точно.
Ответ 6
Вы можете сделать это в O (log n) раз с увеличением экспоненты матрицы:
X = [0 1
1 1]
X ^ n даст вам n-е число фибоначчи в нижнем правом углу; X ^ n можно представить в виде произведения нескольких матриц X ^ (2 ^ i), поэтому, например, X ^ 11 будет X ^ 1 * X ^ 2 * X ^ 8, я <= log_2 (n). И X ^ 8 = (X ^ 4) ^ 2 и т.д., Поэтому не более 2 * log (n) матричных умножений.
Ответ 7
Реализация быстрого вычисления Фибоначчи в Python может быть следующей. Я знаю, что это Python, а не MATLAB/Octave, однако это может быть полезно.
В принципе, вместо того, чтобы снова и снова вызывать одну и ту же функцию Фибоначчи с помощью O (2 n), мы сохраняем последовательность Фибоначчи в списке/массиве с O (n):
#!/usr/bin/env python3.5
class Fib:
def __init__(self,n):
self.n=n
self.fibList=[None]*(self.n+1)
self.populateFibList()
def populateFibList(self):
for i in range(len(self.fibList)):
if i==0:
self.fibList[i]=0
if i==1:
self.fibList[i]=1
if i>1:
self.fibList[i]=self.fibList[i-1]+self.fibList[i-2]
def getFib(self):
print('Fibonacci sequence up to ', self.n, ' is:')
for i in range(len(self.fibList)):
print(i, ' : ', self.fibList[i])
return self.fibList[self.n]
def isNonnegativeInt(value):
try:
if int(value)>=0:#throws an exception if non-convertible to int: returns False
return True
else:
return False
except:
return False
n=input('Please enter a non-negative integer: ')
while isNonnegativeInt(n)==False:
n=input('A non-negative integer is needed: ')
n=int(n) # convert string to int
print('We are using ', n, 'based on what you entered')
print('Fibonacci result is ', Fib(n).getFib())
Вывод для n=12
будет выглядеть следующим образом:
![введите описание изображения здесь]()
Я тестировал время выполнения для n=100
, 300
, 1000
, и код действительно быстрый, мне даже не нужно ждать выхода.