Efficient computation of n choose k in Node.js
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Computing n choose k looks harmless until the numbers get large and the obvious factorial formula starts overflowing or losing precision. In Node.js, the practical approach is to avoid full factorials, exploit the symmetry of the binomial coefficient, and use BigInt whenever exact integer results matter. That combination gives you a helper that is fast enough for most workloads and still mathematically trustworthy.
Avoid the Factorial Formula
The textbook identity
n choose k = n! / (k! * (n-k)!)
is mathematically correct, but it is a poor implementation strategy. Full factorials become enormous very quickly, and JavaScript Number cannot represent all large integers exactly. Even if the final answer would fit in theory, the intermediate factorials can already be far beyond the safe integer range.
The usual replacement is a multiplicative loop that builds the result gradually.
Use Symmetry to Reduce the Work
The first optimization is simple:
C(n, k) = C(n, n-k)
That means you only need to loop min(k, n-k) times.
This is efficient because it does not build giant factorials and only performs the minimum required number of loop iterations.
Why BigInt Matters in Node.js
For small values, Number may seem fine. The problem is that JavaScript integers become unsafe past Number.MAX_SAFE_INTEGER. Binomial coefficients exceed that threshold sooner than many developers expect.
That makes BigInt the right default when the result must be exact. The main rule to remember is that BigInt and Number do not mix automatically in arithmetic. If one side is BigInt, convert the other side deliberately.
This matters especially in API code, where a seemingly correct combinatorics helper can quietly return rounded values if it was built on floating-point math.
Validate Edge Cases Explicitly
A helper like this should state its behavior clearly:
- '
k > nreturns zero.' - '
k = 0ork = nreturns one.' - Negative or non-integer inputs should fail fast.
Those cases are not just defensive programming. They define the contract of the function and keep the behavior stable when the helper is reused elsewhere.
If the function is part of a service, consider whether the return type should always be a string at the API boundary, since JSON does not natively serialize BigInt.
Add Caching Only When Repetition Exists
Many workloads compute the same coefficients repeatedly. In that case, memoization can help. It is not always necessary, but it is useful when the function is called from dynamic programming, probability code, or combinatorial search that revisits the same pairs again and again.
The important point is to add caching because of repeated query patterns, not because it sounds like a general optimization. If every call uses a new pair, the cache only adds memory overhead. Measure the traffic pattern before keeping a long-lived cache in a server process.
Modular Arithmetic Is a Different Problem
Some use cases do not want the exact value at all. They want n choose k mod p, often in competitive programming or large-scale counting problems. That is a different API and should usually live in a separate function rather than being mixed into the exact version.
Keeping exact arithmetic and modular arithmetic separate avoids confusion about what the caller is actually getting back.
Common Pitfalls
The most common mistake is using factorials with Number and assuming the results stay exact. Another is forgetting the symmetry optimization and doing twice as much work as necessary. Teams also run into trouble by mixing BigInt and Number carelessly, or by returning huge exact values through JSON without converting them to strings. A quieter mistake is benchmarking only tiny examples and never noticing that exact combinatorics can become a throughput issue under real concurrency.
Summary
- Avoid the factorial formula for practical Node.js implementations.
- Use symmetry so the loop runs only
min(k, n-k)steps. - Prefer
BigIntwhen exact results matter. - Define edge-case behavior clearly instead of leaving it implicit.
- Treat exact combinatorics and modular combinatorics as separate APIs.
Related reading
- Element implicitly has an 'any' type because expression of type 'string' can't be used to index
- Emberjs - How to test promises and other async behavior?
- endsWith in JavaScript
- Error Cannot find module 'async_hooks' in NodeJs
- Error Cannot find module 'aws-sdk' in NodeJS AWS Lambda Function
- Error Cannot use 'async' on methods without bodies. How to force async child overrides?
- Error, Can''t set headers after they are sent to the client
- error Could not get BatchedBridge, make sure your bundle is packaged properly on start of app
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.