Memoization

Memoization is an optimization technique where we the store results of previous expensive operations and use them without repeating the operation. It helps us speed up the solution significantly. When we have problems where we can have repetitive sub problems, we can easily apply this technique to store those results and use them later on without repeating the steps. Since PHP has a great support for associative arrays and dynamic array properties, we can cache the results without any problems. One thing we have to remember is that though we are saving time by caching the results, we will require more memory to store these results in the cache. So, we have to make the trade-off between space and memory. Now, let's revisit Chapter 5, Applying Recursive Algorithms - Recursion, for our recursive example of generating Fibonacci numbers. We will just modify that function with a counter to know how many times the function is called and the running time of the function to get the thirtieth Fibonacci number. Here is the code for this:

$start Time = microtime(); 
$count = 0;

function fibonacci(int $n): int {
global $count;
$count++;
if ($n == 0) {
return 1;
} else if ($n == 1) {
return 1;
} else {
return fibonacci($n - 1) + fibonacci($n - 2);
}
}

echo fibonacci(30) . " ";
echo "Function called: " . $count . " ";
$endTime = microtime();
echo "time =" . ($endTime - $startTime) . " ";

This will have the following output in the command line. Note that timing and results may vary from one system to the other or from one version of PHP to the other. It completely depends on the where the program is running:

1346269
Function called: 2692537
time =0.531349

The first number 1346269 is the thirtieth Fibonacci number, and the next line shows that the fibonacci function was called 2692537 times during the generation of the thirtieth number. The whole process took 0.5 seconds (we are using the microtime function of PHP). If we were generating the fiftieth Fibonacci number, the function call count would be more than 40 billion times. That is one big number. However, we know from our Fibonacci formula that when we are calculating n. We are doing it through n-1 and n-2; those are already calculated in the previous steps. So, we are repeating the steps, and hence, it is costing us time and efficiency. Now, let's store the Fibonacci results in an indexed array, and we will check whether the Fibonacci number we are looking for is already calculated or not. If it is calculated, we will use it; otherwise, we will calculate that and store the result. Here is the modified code for generating Fibonacci numbers using the same recursive process, but with help of memorization:

$startTime = microtime(); 
$fibCache = [];
$count = 0;

function fibonacciMemoized(int $n): int {
global $fibCache;
global $count;
$count++;
if ($n == 0 || $n == 1) {
return 1;
} else {

if (isset($fibCache[$n - 1])) {
$tmp = $fibCache[$n - 1];
} else {
$tmp = fibonacciMemoized($n - 1);
$fibCache[$n - 1] = $tmp;
}

if (isset($fibCache[$n - 2])) {
$tmp1 = $fibCache[$n - 2];
} else {
$tmp1 = fibonacciMemoized($n - 2);
$fibCache[$n - 2] = $tmp1;
}

return $tmp + $tmp1;
}
}

echo fibonacciMemoized(30) . " ";
echo "Function called: " . $count . " ";

$endTime = microtime();
echo "time =" . ($endTime - $startTime) . " ";

As we can see from the preceding code, we have introduced a new global variable called $fibCache, which will store the calculated Fibonacci numbers. We also check whether the number we are looking for is already in the array or not. We do not calculate the Fibonacci if the number is already stored in our cache array. If we run this code now, we will see the following output:

1346269
Function called: 31
time =5.299999999997E-5

Now, let's examine the result. The thirtieth Fibonacci number is the same as we had the last time. However, look at the function call count. It is just 31 instead of 2.7 million calls. Now, let's look at the time. We have taken only 0.00005299 seconds, which is 10,000 times faster than the non-memoized version.

With a simple example, we can see that we can optimize our solutions by utilizing memoization where it is applicable. One thing we have to remember is that memoization will work better where we have repeating sub problems or where we have to consider the previous calculation to compute the current or future calculation. Although memoization will take extra space to store the partially computed data, utilization of memoization can increase performance by a big margin

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset