Never calculate factorials; they grow too fast. Instead, calculate the desired result. In this case, you need binomial numbers that have an incredibly simple geometric construction: you can construct the triangular Pascal nickname as you need it and do it with simple arithmetic.
Start with [1] and [1,1]. The next line has [1] at the beginning, [1 + 1] in the middle and [1] at the end: [1,2,1]. Next line: [1] at the beginning, the sum of the first two terms at spot 2, the sum of the following two terms at the fifth point and [1] at the end: [1,3,3,1]. The next line: [1], then 1 + 3 = 4, then 3 + 3 = 6, then 3 + 1 = 4, then [1] at the end, and so on and so forth. As you can see, there are no factorials, logarithms and even multiplications: just super fast addition with pure integers. So simple, you can create a massive lookup table manually.
And you have to.
Never calculate in the code what you can calculate manually, but simply include as constants for immediate search; in this case, writing out a table where the number of elements is approximately equal to n = 20 is absolutely trivial, and then you can just use this as your “initial LUT” and maybe never even access the high lines.
But, if you need them, or even more, then because you cannot create an infinite lookup table, you compromise: you start with a predefined LUT and a function that can “fill in” some term that you need . not yet:
module.exports = (function() { // step 1: a basic LUT with a few steps of Pascal triangle var binomials = [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1], [1,5,10,10,5,1], [1,6,15,20,15,6,1], [1,7,21,35,35,21,7,1], [1,8,28,56,70,56,28,8,1], ... ]; // step 2: a function that builds out the LUT if it needs to. function binomial(n,k) { while(n >= binomials.length) { let s = binomials.length; let nextRow = []; nextRow[0] = 1; for(let i=1, prev=s-1; i<s; i++) { nextRow[i] = binomials[prev][i-1] + binomials[prev][i]; } nextRow[s] = 1; binomials.push(nextRow); } return binomials[n][k]; } return binomial; }());
Since this is an array of integers, the amount of memory is small. For a lot of work related to binomes, we really don’t even need more than two bytes per integer, which makes this table a brief search: we don’t need more than 2 bytes until you need binomes larger than n = 19, and the full search table is up to n = 19 occupies 380 bytes. This is nothing compared to the rest of your program. Even if we allow 32-bit values, we can get up to n = 35 in just 2380 bytes.
Thus, the search is fast: either O (constant) for previously calculated values, (n * (n + 1)) / 2 steps, if we don’t have LUT at all (in a large record O it will be O (n²), but notation Big O is almost never the right measure of complexity), and somewhere in between is for the terms we need that aren't in the LUT yet. Run some tests for your application that will tell you how big your initial LUT should be, just hard code that (seriously. These are constants, these are exactly the values ​​that need to be hard-coded) and keep the generator around just in case.
However, remember that you are in the land of JavaScript, and you are limited to the numeric type of JavaScript: integers reach only 2 ^ 53 , except that the property is an integer (each n has a separate m=n+1 such that mn=1 ) is not guaranteed. This is unlikely to ever be a problem: once we reach this limit, we are dealing with binomial coefficients that you should never use.