0

How can I get all n-digit numbers whose sum of digits equals to given sum? I need the fastest solution because n can be equal with 9 and sum can be equal with 1000.

I have implemented the solution below but it's too slow...

List<int> l = new List<int>();
void findNDigitNumsUtil(int n, int sum, char[] ou, int index)
{
    if (index > n || sum < 0)
        return;
    if (index == n)
    {
        if (sum == 0)
        {
            ou[index] = '\0';
            string s = new string(ou);
            l.Add(Int32.Parse(s));
        }

        return;
    }

    for (int i = 0; i <= 9; i++)
    {
        ou[index] = (char)(i + '0');
        findNDigitNumsUtil(n, sum - i, ou,
                                index + 1);

    }
}

void findNDigitNums(int n, int sum)
{
    char[] ou = new char[n + 1];
    for (int i = 1; i <= 9; i++)
    {
        ou[0] = (char)(i + '0');
        findNDigitNumsUtil(n, sum - i, ou, 1);
    }
}
  • 1
    would fit better to https://codereview.stackexchange.com/ – Eser Jun 18 '18 at 15:00
  • 1
    *it's too slow...* This question may not be the best fit for this site, but if you would like help with performance, it's typically a good idea to provide metrics and your expectations. – Dan Wilson Jun 18 '18 at 15:34
  • Try following : int n = 1234567; int sum = 0; while (n > 0) { sum += n % 10; n /= 10; } – jdweng Jun 18 '18 at 16:34
  • 5
    You said "n can be equal with 9 and sum can be equal with 1000" but that doesn't make any sense. The largest possible nine digit number sum is 999999999 and its digits sum to 81. You can't possibly get anywhere even close to a digit sum of 1000 with only 9 digits! – Eric Lippert Jun 18 '18 at 18:10

2 Answers2

6

I need the fastest solution

No, you need a fast-enough solution. You are probably unwilling to spend even a million dollars on custom hardware to get the fastest possible solution.

How can I get all n-digit numbers whose sum of digits equals to given sum?

Here, I'll give you the solution for a slightly different problem:

What are all the sequences of n digits drawn from 0-9 that sum to sum?

This is different because this counts 01 and 10 as sequences of length two that sum to 1, but 01 is not a two-digit number.

I'll give you a hint for how to solve this easier problem. You then take that solution and adapt it to your harder problem.

First, can you solve the problem for one-digit numbers? That's pretty easy. The one-digit numbers whose digits sum to n are the digit n if n is 0 through 9, and there is no solution otherwise.

Second: Suppose n > 1. Then the n-digit numbers that sum to sum are:

  • 0 followed by all the n-1 digit numbers that sum to sum
  • 1 followed by all the n-1 digit numbers that sum to sum-1
  • 2 followed by all the n-1 digit numbers that sum to sum-2
  • ...
  • 9 followed by all the n-1 digit numbers that sum to sum-9

Write an implementation that solves that problem, and then adapt it to solve your problem.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
0

You can treat n-digit number as an array of n digits. Then you can increment a particular number to the next number that also adds up to the sum. Stepping through all the next answers, you have generated all possible combinations.

Using a generator to yield each n-digit combination as an IEnumerable<int> (in fact, an int[]), you start with the "smallest" n-digit combination that yields the sum, and go through each one.

IEnumerable<IEnumerable<int>> DigitsToSum(int n, int sum) {
    if (sum > 9 * n)
        yield return Enumerable.Empty<int>();
    else {
        var ans = new int[n];

        void distribute(int wsum, int downto) {
            for (var j1 = n - 1; j1 > downto; --j1) {
                if (wsum > 9) {
                    ans[j1] = 9;
                    wsum -= 9;
                }
                else {
                    ans[j1] = wsum;
                    wsum = 0;
                }
            }
        }

        ans[0] = Math.Max(1, sum-9*(n-1));
        distribute(sum-ans[0], 0);

        bool nextAns() {
            var wsum = ans[n-1];
            for (var j1 = n - 2; j1 >= 0; --j1) {
                wsum += ans[j1];

                if (ans[j1] < Math.Min(9, wsum)) {
                    ++ans[j1];
                    distribute(wsum - ans[j1], j1);
                    return true;
                }
            }
            return false;
        }

        do {
            yield return ans;
        } while (nextAns());
    }
}

This is tremendously faster than my recursive double generator solution (somewhat like @EricLippert's suggestion) to iterate over all possibilities (e.g. using Count()).

You can put the digits back together to get a final numeric string for each number:

var ans = DigitsToSum(n, sum).Select(p => String.Join("", p));
NetMage
  • 26,163
  • 3
  • 34
  • 55