简体   繁体   中英

Javascript factorial function memoization

I'm trying to use the factorial function with memoization. I have taken the max value from the object to reduce the number of recursive calls made. But the problem is the first call is I don't know whether this is optimized or not since the first call is pretty expensive. Any insights on this will be great.

let cache = {0: 1};
function factMemoize(key) {
    if (!cache[key]) {
        let maxVal = Object.keys(cache).reduce(function (a, b) {
            return Math.max(a, b);
        });
        console.log(maxVal);
        while (key >= maxVal) {
            cache[key] = key * factMemoize(key - 1);
            maxVal++;
        }
    }
    return cache[key];
}

You don't buy much from memoizing this since you only use each value once. After you've called the function you do have the cache for a second call, but we often think of memoizing as something that happens in a cache that only exists during the function. For something like that, calculating Fibonacci numbers is a classic example where memoizing is a huge improvement over the naive recursive function.

Having said that, in your function it's not clear why you are using an object for your cache and then searching it. You can just use an array where the indexes will be the number you're looking for calculate. You don't need to search it, just start with the number and recursively call the next lower one. If there's a cache it, it will return. For example:

 let cache = [1]; function factMemoize(key) { if (!cache[key]) { cache[key] = key * factMemoize(key - 1) } else { // just to demo cache: console.log("cache hit:", key) } return cache[key] } // only hits cache at the end console.log("6! = ", factMemoize(6)) // second call benefits from cache: console.log("8! = ", factMemoize(8))

As @mark-meyer mentioned in this thread, there is no advantage coming from memoizing the results since each value will be calculated only one time during computation. The solution Mark offered is great for reusing the function in a later time by re-calling factorials with same or different values. In that case, you can speed up the process and reduce the time complexity by reusing existing results.

Here is how it can look like in a closure:

function factorialFn() {
  const cache = [];

  return function _factorial() {
    if (n < 2) {
      return 1;
    }
    if (cache[n]) {
      return cache[n];
    }
    return cache[n] = n * _factorial(n - 1);
  }
}

Then, you can use it like so:

const factorial = factorialFn();

factorial(5); // 120
factorial(7); // 5040


At the first call, it will calculate factorial(5) and save it in cache for future reference.

At the second call, factorial(7) will execute 7 * factorial(6) ,
which factorial(6) is basically 6 * the cached value of factorial(5)

function factorial(n, cache = {}) {
    if (n < 2) return 1;
    if (n in cache) return cache[n];
    return cache[n] = n * factorial(n - 1, cache);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM