Fibonacci In Prolog
Title: Fibonacci in Prolog
Date: 2017-09-26T00:00:00
Tags: Prolog, Memoization
Authors: Henry Brooks
I was looking over Exercise 11.3 at Learn Prolog Now and realized that the problem actually implements memoization to calculate its result. I thought I would try modifying the problem to see if it could be used to solve the Fibonacci equation.
The sigma problem asks you to create a sigmares(N,S)
result for every sigma(N,S)
and assert
this value to the database.
:- dynamic sigmares/2.
sigmares(1, 1).
sigma(X, Y) :-
sigmares(X, Y),
!.
sigma(X, Y) :-
Xp is X - 1,
sigma(Xp, R),
Y is R + X,
assert(sigmares(X, Y)).
We can quickly check that this function properly updates the database.
sigma(10,X), listing.
:- dynamic sigmares/2.
sigmares(1, 1).
sigmares(2, 3).
sigmares(3, 6).
sigmares(4, 10).
sigmares(5, 15).
sigmares(6, 21).
sigmares(7, 28).
sigmares(8, 36).
sigmares(9, 45).
sigmares(10, 55).
To solve the Fibonacci problem I modified the code to match the Fibonacci equations structure.
:- dynamic fib/2.
fibs(0,1).
fibs(1,1).
fib(N,S):-
fibs(N,S),
!.
fib(N,S):-
N1 is N-1,
N2 is N-2,
fib(N1,S1),
fib(N2,S2),
S is S1 + S2,
assert(fibs(N,S)).
I then tested to see if the results looked correct.
fib(15,X), listing.
:- dynamic fibs/2.
fibs(0, 1).
fibs(1, 1).
fibs(2, 2).
fibs(3, 3).
fibs(4, 5).
fibs(5, 8).
fibs(6, 13).
fibs(7, 21).
fibs(8, 34).
fibs(9, 55).
fibs(10, 89).
fibs(11, 144).
fibs(12, 233).
fibs(13, 377).
fibs(14, 610).
fibs(15, 987).
I was actually really surprised at how quickly the program ran given that I never really consider Prolog
for numerical calculations.
I ended up timing fib(10000)
to compare to Racket
.
get_time(T1),fib(10000,_),get_time(T2), T3 is T2 - T1, write(T3).
> 0.05961465835571289
#lang racket
(define (fib n)
(define cache (make-vector (add1 n) -1))
(vector-set! cache 0 0)
(vector-set! cache 1 1)
(define (mfib i)
(if (= (vector-ref cache i) -1)
(vector-set! cache i (+ (mfib (- i 1)) (mfib (- i 2))))
(vector-ref cache i))
(vector-ref cache i))
(mfib n))
(define T1 (current-inexact-milliseconds))
(fib 10000)
(define T2 (current-inexact-milliseconds))
(- T2 T1)
> 5.401123046875
Racket
is calculating it’s time in milliseconds so the comparison is actually around 5.9
to 5.4
. With these numbers it seems like Prolog
ended up taking a around the same time that Racket
took to calculate fib(10000)
. Considering that Prolog
isn’t normally considered a numerical language I think that these results are impressive. I will definitely be looking for ways to test Prolog
with more memoization
problems.
Note
I removed the memoization
component from the Prolog
code and ran fib(40)
to see how much time was saved.
:- use_module(library(clpfd)).
fib(N,S):-
N1 is N-1,
N2 is N-2,
fib(N1,S1),
fib(N2,S2),
S is S1 + S2.
%assert(fibs(N,S)).
get_time(T1),fib(40,_),get_time(T2), T3 is T2 - T1, write(T3).
> 50.405715465545654
#lang racket
(define (fib n)
(cond [(<= n 1) 1]
[else (+ (fib (- n 1)) (fib (- n 2)))]))
(time (fib 40))
> cpu time: 6320 real time: 6255 gc time: 32
The difference without memoization
was effectively an order of magnitude. This really highlights for me the importance of memoization
for Prolog
programs.