I might make the recursion a bit more explicit. To my mind there are a number of different base-cases, for various low values of n
and s
:
- if
s < 0
, then there are no results
- if
s == 0
, then the only result is a string of n
zeroes
- if
n == 1
, then
- if
s < 10
, then the only result is the digit s
- otherwise, there are no results
The recursive case involves taking each digit as a potential first digit, then joining it with each of the results involved in recursing, taking that amount from the total and using a digit count one smaller.
Here's an implementation of that:
const subsetSum = (n, s) =>
s < 0
? []
: s == 0
? ['0' .repeat (n)]
: n == 1
? s < 10 ? [String(s)] : []
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)
console .log (
subsetSum (3, 17)
) //~> ["089", "098", "179", "188", "197", "269", "278", ..., "971", "980"] (63 entries)
.as-console-wrapper {min-height: 100% !important; top: 0}
Given your comment about licenses, I assume that it's really strings of digits you want and not numbers. That's what this returns. If you want numbers, you will need to remove all those that start with 0
and convert the strings to numbers. (Thus, 89
would not be included in subset(17, 3)
even thought "089"
is a legitimate digit string, because 89
is only a two-digit number.)
Update
I just realized that the s == 0
case can be subsumed in the recursive one. So it's actually a bit simpler:
const subsetSum = (n, s) =>
s < 0
? []
: n == 1
? s < 10 ? [String(s)] : []
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)
Or, phrased slightly differently, as
const subsetSum = (n, s) =>
s < 0 || (n <= 1 && s >= 10)
? []
: n == 1
? [String(s)]
: // else
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .flatMap (
k => subsetSum (n - 1, s - k) .map (p => k + p)
)