6

I've created a memoized function of the recursive version of fibonacci. I use this as an example for other kinds of functions that would use memoization. My implementation is bad since if I include it in a library, that means that the global variable is still seen..

This is the original recursive fibonacci function:

function fibonacci($n) {
  if($n > 1) {
    return fibonacci($n-1) + fibonacci($n-2);
  }
  return $n;
}

and I modified it to a memoized version:

$memo = array();
function fibonacciMemo($n) {
  global $memo;
  if(array_key_exists($n, $memo)) {
    return $memo[$n];
  }
  else {
    if($n > 1) {
      $result = fibonacciMemo($n-1) + fibonacciMemo($n-2);
      $memo[$n] = $result;
      return $result;
    }
    return $n;
  }
}

I purposely didn't use the iterative method in implementing fibonacci. Is there any better ways to memoize fibonacci function in php? Can you suggest me better improvements? I've seen func_get_args() and call_user_func_array as another way but I can't seem to know what is better?

So my main question is: How can I memoize fibonacci function in php properly? or What is the best way in memoizing fibonacci function in php?

catzilla
  • 1,901
  • 18
  • 31

5 Answers5

6

Well, Edd Mann shows an excellent way to implement a memoize function in php in His post

Here is the example code (actually taken from Edd Mann's post):

$memoize = function($func)
{
    return function() use ($func)
    {
        static $cache = [];

        $args = func_get_args();

        $key = md5(serialize($args));

        if ( ! isset($cache[$key])) {
            $cache[$key] = call_user_func_array($func, $args);
        }

        return $cache[$key];
    };
};

$fibonacci = $memoize(function($n) use (&$fibonacci)
{
    return ($n < 2) ? $n : $fibonacci($n - 1) + $fibonacci($n - 2);
});

Notice that the global definition it's replaced thanks to function clousure and PHP's first-class function support.

Other solution:

You can create a class containing as static members: fibonnacciMemo and $memo. Notice that you don't longer have to use $memo as a global variable, so it won't give any conflict with other namespaces. Here is the example:

class Fib{
    //$memo and fibonacciMemo are static members
    static $memo = array();
    static function fibonacciMemo($n) {
      if(array_key_exists($n, static::$memo)) {
        return static::$memo[$n];
      }
      else {
        if($n > 1) {
          $result = static::fibonacciMemo($n-1) + static::fibonacciMemo($n-2);
          static::$memo[$n] = $result;
          return $result;
        }
        return $n;
      }
    }
}

//Using the same method by Edd Mann to benchmark 
//the results

$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000249

$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000016 (now with memoized fibonacci)

//Cleaning $memo
Fib::$memo = array();
$start = microtime(true);
Fib::fibonacciMemo(10);
echo sprintf("%f\n", microtime(true) - $start);
//outputs 0.000203 (after 'cleaning' $memo)

Using this, you avoid the use of global and also the problem of cleaning the cache. Althought, $memo is not thread save and the keys stored are no hashed values. Anyways, you can use all the php memoize utilites such as memoize-php

Robin Curbelo
  • 1,323
  • 1
  • 11
  • 21
  • Yeah, I saw this post too, its very informative and detailed.. but the way its calling the function is different: `$fibonacci(10)`... does this make a significant change or difference than the usual function call `fibonacci(10)`? And I believe this one really is not memoization but caching in general and also supported by one of the comments in the post you shared: also on wiki: Although related to caching, memoization refers to a specific case of this optimization, distinguishing it from forms of caching such as buffering or page replacement. – catzilla Mar 05 '15 at 10:12
  • There is no difference between `$fibonacci(10)` and `fibonacci(10)`, the first one is an object that its value is an anonymous function (not a function declaration like the second one). I saw that comment on the post, and the problem is with memory: *there's no way to clear the cache* and *ate up a lot of memory*. Although it is neither [buffering](http://en.wikipedia.org/wiki/Data_buffer) nor that [page replacement](http://en.wikipedia.org/wiki/Page_replacement_algorithm). – Robin Curbelo Mar 05 '15 at 15:49
  • Thanks for the information, your second solution works fine for me.. I'm just inconvenient using static classes methods for fibonacci problem..But I think using static property greatly reduces calcutation time for the preceding calls for that.. And for the first solution, I think I'll try to test and investigate on that one.. I think your answer deserve the vote.. Thanks anyways.. :) – catzilla Mar 06 '15 at 03:58
3

i think... this should to to memoize a fibonacci:

function fib($n, &$computed = array(0,1)) {
    if (!array_key_exists($n,$computed)) {
        $computed[$n] = fib($n-1, $computed) + fib($n-2, $computed);   
    }
    return $computed[$n];
}

some test

$arr =  array(0,1);
$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000068

$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000005

//Cleaning $arr
$arr =  array(0,1);
$start = microtime(true);
fib(10,$arr);
echo sprintf("%f\n", microtime(true) - $start);
//0.000039
Javier Neyra
  • 1,239
  • 11
  • 12
  • Hi.. yes this method works too.. and it has also good points.. but the problem with this is that you need to include the parameter `$arr` to every declaration of the function `fib()`.. its best that `$arr` should be hidden during the use of the function `fib()`.. – catzilla Jun 22 '15 at 02:21
  • not really, if you look at the function signature the array is declared there, if you dont pass the array to the function it gets reinitialized in every call, so, it is hidden, but, if you plan to use the function more than once, well, you can declare your array and use it, you can even, serialize it, store it and reload it later, using the same function without modifications... – Javier Neyra Jun 22 '15 at 04:22
  • Hi, I tried this method and yes, it worked great.. maybe I misunderstood the way the `$computed` parameter is used but it worked great! so far, this is the most simple memoized Fibonacci function I encountered..! I will try to benchmark this function against other implementation of fibonacci and compare the results... thanks for your answer! – catzilla Jun 24 '15 at 01:42
1

This's an implementation of memoize a fibonacci:

function fib(int $n, array &$memo = [0,1,1]) : float {

    return $memo[$n] ??  $memo[$n] = fib($n-1, $memo) + fib($n-2, $memo);

}

Call

echo fib(20); // 6765
mwafi
  • 3,946
  • 8
  • 56
  • 83
  • cool to see answers that use latest php features; as of writing this question, the latest php version at the time is 5.7 which does not have this null coalescing operator. – catzilla May 17 '23 at 11:03
0

Another solution:

function fib($n, &$memo = []) {

    if (array_key_exists($n,$memo)) {
        return $memo[$n];
    }

    if ($n <=2 ){
        return 1;
    }

    $memo[$n] = fib($n-1, $memo) + fib($n-2, $memo);

    return $memo[$n];
}

Performance:

$start = microtime(true);
fib(100);
echo sprintf("%f\n", microtime(true) - $start);
// 0.000041
-1
            function fibMemo($n)
            {

                static $cache = [];
                //print_r($cache);
                if (!empty($cache[$n])) {
                    return $cache[$n];
                } else {
                    if ($n < 2) {
                        return $n;
                    } else {
                        $p   = fibMemo($n - 1) + fibMemo($n - 2);
                        $cache[$n]  = $p;
                        return $p;
                    }
                }
            }
            echo fibMemo(250);
  • 1
    Please add some explanation to your answer such that others can learn from it. Also, please share what makes your solution worth an answer for a problem that is more than seven years old – Nico Haase Nov 22 '22 at 13:56