Пролог; попробуйте сделать Фибоначчи более эффективным?

Это логическое программирование действительно делает танец на коленях на моих императивных навыках программирования. Это домашнее задание, так что, пожалуйста, не бросай мне ответ. Вот что у меня есть:--4-->

fibo(N,1) :-
   N < 2,
   !. 
fibo(N,R) :-
   N1 is N-1,
   N2 is N-2,
   fibo(N1,R1),
   fibo(N2,R2),
   R is R1+R2.

Я должен сделать другую функцию, которая выглядит так;fib(N,Value,LastValue). N - это n-е число, а value-возвращаемое значение. Я не понимаю,как я могу переписать это, используя накопление. И поскольку он отсчитывает назад, я не вижу, как он может" знать " последнее значение перед вычислением что угодно. : s любой вход приветствуется.

5 ответов


я мог бы опубликовать здесь решение, но так как это домашнее задание, оно было бы контрпродуктивным. Вместо этого, вот зацепка:

проблема с версией Фибоначчи, которую вы перечислили, заключается в том, что она неэффективна. Каждый вызов fibo/2 причиной два вызовы, но некоторые из этих вызовов вычисляют значения тех же чисел Фибоначчи. Например, в псевдо-коде:

(a) fibo(4) -> fibo(3), fibo(2)
(b) fibo(3) -> fibo(2), fibo(1)
(c) fibo(2) -> fibo(1), fibo(0) % called from (a)
(d) fibo(2) -> fibo(1), fibo(0) % called from (b), redundant

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

fib(1,1,0).
fib(2,1,1).

я оставлю рекурсивный случай вам.


для нетерпеливых

вот рекурсивный случай:

fib(N, Val, Last) :-
  N > 2,
  N1 is N - 1,
  fib(N1, Last, Last1), % single call with two output arguments, 
                        % instead of two calls with one output argument
  Val is Last + Last1.

посмотреть связанные обсуждения:

обобщение последовательности Фибоначчи с помощью Sicstus Prolog

и рассмотрим очень хорошее решение ony, используя ограничения конечной области оттуда.


возможно, использование хвостовой рекурсии-хороший вариант

изменить: Вместо разбиения fib (6) на fib(5)+fib(4) Вы можете попробовать что-то вроде fib(6) = fib(6,0,0) первый параметр-это количество шагов, когда он достигает 0, вы останавливаетесь, второй параметр-последнее вычисленное вами значение, а третий параметр-это значение для вычисления, которое равно сумме текущих второго и третьего параметров (за исключением первого шага, в котором 0 + 0 будет 1)

Так чтобы вычислить, вы устанавливаете второй параметр при каждом вызове и acumulate в третьем so fib(6,0,0) => fib(5,0,1) => fib(4,1,1) => fib(3,1,2) => fib(2,2,3) => fib(1,3,5) => fib(0,5,8), затем вы возвращаете 8

в этом методе вам фактически не нужно сохранять в стеке возврат адреса, избегая переполнения стека


помните, что есть еще один способ вычисления последовательности Фибоначчи: начиная с базового случая и двигаясь вверх.

прямо сейчас, чтобы вычислить fib(n) добавить fib(n-1) и fib(n-2). Вместо этого переверните это и вычислите fib(0) и fib(1) на основе определения последовательности Фибоначчи, и выстроить из этого.


У вас почти уже есть. Просто перепишите:

fibo(N, Value) :-
N1 is N-1, N2 is N-2,
 fibo(N1, LastValue),fibo(N2, SecondToLastValue),
 Value is LastValue + SecondToLastValue.

С точки зрения

fibo2(N, Value, LastValue):- ...

Я не понимаю, как я могу переписать это используя накопление

просто не надо, это не нужно (хотя это можно сделать).