// More verbose and integer caching // O(n^2) first call, best case in subsequent calls: O(1) let iterations = 0; const nCr = (n: number, r: number) => { if (r == 0 || r == n) { return 1; } nCr[n] = nCr[n] || []; if (!nCr[n][r]) { nCr[n][r] = nCr(n-1, r-1) + nCr(n-1, r); iterations++; } return nCr[n][r]; }; console.log("Recursive, caching:") console.log(`50C20 = ${nCr(50, 20)}, iterations: ${iterations}`); iterations = 0; console.log(`50C20 = ${nCr(50, 20)}, iterations: ${iterations}`); iterations = 0; // Simple non-caching iterative. // exactly O(r), always const nCr2 = (n: number, r: number) => { let product = 1; for (let i = 1; i <= r; i++) { product *= (n + 1 - i) / i; iterations++; } return product; } console.log("Iterative, non-caching") console.log(`50C20 = ${nCr2(50, 20)}, iterations: ${iterations}`); iterations = 0; console.log(`50C20 = ${nCr2(50, 20)}, iterations: ${iterations}`); /* * Outputs: * * Recursive, caching: * 50C20 = 47129212243960, iterations: 600 * 50C20 = 47129212243960, iterations: 0 * Iterative, non-caching * 50C20 = 47129212243960, iterations: 20 * 50C20 = 47129212243960, iterations: 20 */