2

Given a min & max I'd like to find every combination of numbers in that range that add up to a given total using a specified number of bins (reusing numbers is OK). # of bins will approach but not exceed 32, so bitwise is awesome if that's an option.

For example:

input:
min = 1
max = 4
total = 9
bins = 4

output:
1,1,3,4
1,2,2,4
1,2,3,3
2,2,2,3

My crappy, inefficient solution:

function arrSum(arr) {
  var sum = 0;
  for (var i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
  return sum;
}

function incrementComboArr(arr, maxNum) {
  var i = arr.length - 1;
  while (i >= 0) {
    if (++arr[i] > maxNum) {
      i--;
    } else {
      for (var j = i + 1; j < arr.length; j++) {
        arr[j] = arr[i];
      }
      break;
    }
  }
  return i === -1;
}

getSetCombos = function (minNum, maxNum, target, bins) {
  var iters = 0;
  var solutions = [];
  var arr = [];
  var i;
  for (i = 0; i < bins; i++) {
    arr[i] = minNum;
  }
  while (true) {
    iters++;
    var sum = arrSum(arr);
    if (sum === target) {
      solutions.push(arr.slice());
    }
    if (incrementComboArr(arr, maxNum)) break;
  }
  console.log(iters);
  return solutions;
};

The problem with my solution is that it increments by 1 even when the delta between the current iteration & the target value is deterministic. Also, it doesn't know when to stop. (I could determine the last feasible solution by doing something like if arr[0] > ~~(total/bins) but that seems wonky. Given that the series is a sequence, I know there has to be some pattern I'm not taking advantage of, but I can't think of it. Code/ideas/lecture notes all welcome!


RESULTS

After converting both answers to ES5 (edits welcome), the first solution clocks in around 5ms & the second one (recursive) around 500ms. I'll mark this as answered in a day.

Here's the code I used for each:

//Code translated from Spektre
subsetSum = function (minNum, maxNum, target, bins) {
  var start = new Date();
  var solutions = [];
  var arr = [];
  var i;
  var s;
  var loop = true;
  for (i = 0; i < bins; i++) {
    arr[i] = minNum;
  }
  s = minNum * bins;
  while (loop) {
    if (s === target) {
      solutions.push(arr.slice());
    }
    for (i = bins;;) {
      i--;
      arr[i]++;
      s++;
      for (var j = i + 1; j < bins; j++) {
        s+= arr[i]-arr[j];
        arr[j]=arr[i];
      }
      if ((s<=target)&&(arr[i]<=maxNum)) break;
      if (!i) {
        loop = false;
        break;
      }
      s+=maxNum-arr[i];
      arr[i]=maxNum;
    }
  }
  return new Date() - start;
};


//Code translated from karthik
subsetSumRecursive = function(minNum, maxNum, target, bins) {
  var start = new Date();
  var solutions = [];
  var arr= [], i;
  var totalBins = bins;
  for (i = 0; i < bins; i++) {
    arr[i] = minNum;
  }
  target -= minNum * bins;
  countWays(target, bins, arr, 0);
  return new Date() - start;
  function countWays(target, binsLeft, arr) {
    if (binsLeft === 1) {
      arr[totalBins-1] += target;
      if (arr[totalBins-1] <= maxNum) {
        solutions.push(arr.slice());
      }
      return;
    }
    if (target === 0 && arr[totalBins-binsLeft] <= maxNum) {
      solutions.push(arr.slice());
      return;
    }
    var binsCovered = 0;
    if (target >= binsLeft) {
      var newArr = arr.slice();
      while (binsCovered < binsLeft) {
        newArr[totalBins - binsCovered -1]++;
        binsCovered++;
      }
      countWays(target - binsLeft, binsLeft, newArr);
    }
    countWays(target, binsLeft-1, arr);
  }
};

subsetSum(1,4,100,32);
subsetSumRecursive(1,4,100,32);
Martin Thorsen Ranang
  • 2,394
  • 1
  • 28
  • 43
Matt K
  • 4,813
  • 4
  • 22
  • 35
  • only other way i can think of is to model it as a Constraint Satisfaction Problem, min/max is the domain, bins the number of variables and total the constraint. depending on the input the CSP may or may not be more efficient than iteration, and its also recursive in nature. – softwarenewbie7331 Jul 08 '15 at 05:29
  • Are you talking about something like this? http://stackoverflow.com/a/30870577/3155110 If so, it's about 2x slower for smaller sets, although not sure about big O timing. If not, shoot me a link & I'll dig into it. – Matt K Jul 08 '15 at 05:36
  • Nope, and you can just wiki CSPs and read up, its recursive with backtracking though.. for smaller sets (low bin count) you can just count up from 1,1,1 -> 1,1,2 -> 1,1,3 -> 1,2,1 until 3,3,3 then print out whenever u hit total. – softwarenewbie7331 Jul 08 '15 at 05:39
  • @MattK are the bins identical? I mean if the total is `4` then `1,2,1` and `1,1,2` are considered different or same – Karthik Jul 08 '15 at 06:11
  • @MattK and do you have any constraint like every bin should have at least `1` ? – Karthik Jul 08 '15 at 06:13
  • @karthik, `1,2,1` and `1,1,2` are considered the same & every bin needs a number. – Matt K Jul 08 '15 at 12:31
  • I can give a simple recursive `O(N^2)` `Dynamic Programming` solution. Can you convert it to iterative solution yourself? – Karthik Jul 08 '15 at 12:36
  • @MattK what is the answer you are expecting for `subsetSum(1,4,100,32);`? – Karthik Jul 08 '15 at 16:33
  • @MattK I updated my answer with new code. The code can be optimised more but when I am missing some small detail and it's not giving exact output. By that Just test this code and let me know if there is any improvement. – Karthik Jul 08 '15 at 17:12

2 Answers2

1

In C++ I would do it like this:

//---------------------------------------------------------------------------
AnsiString subset_sum(int min,int max,int sum,int N)
    {
    AnsiString txt="",lin; int cnt=0;   // output text and number of subsets fond
    int i,s,a[32];                      // iterator,actual sum,actual permutation

    // init nested for
    for (i=0;i<N;i++) a[i]=min; s=min*N;
    // nested for
    for (bool _loop=true;_loop;)
        {
        // if correct sum remember it to txt and update cnt
        if (s==sum)
            {
            for (lin="",i=0;i<N;i++) lin+=AnsiString(a[i])+" "; txt+=lin+"\r\n";
            cnt++;
            }
        // nested for step lequeal
        for (i=N;;)
            {
            i--; a[i]++; s++;
            if ((s<=sum)&&(a[i]<=max)) break;
            if (!i) { _loop=false; break; }
            s-=a[i]; a[i]=min; s+=a[i];
            }
        }

    txt+=AnsiString(cnt)+" subsets found\r\n";
    return txt;
    }
//---------------------------------------------------------------------------
  • It is based on nested for in C++
  • and added condition to ignore higher sums
  • it could be speeded up by computing the last a[N-1]=sum-s
  • for 32 bins is this also too slow ...
  • but way faster then your approach

[edit1] remove (position shuffles) duplicates

//---------------------------------------------------------------------------
AnsiString subset_sum(int min,int max,int sum,int N)
    {
    AnsiString txt="",lin; int cnt=0;   // output text and number of subsets fond
    int i,j,s,a[32];                        // iterator,actual sum,actual permutation
    // init nested for
    for (i=0;i<N;i++) a[i]=min; s=min*N;
    // nested for
    for (bool _loop=true;_loop;)
        {
        // if correct sum remember it to txt and update cnt
        if (s==sum)
            {
            for (lin="",i=0;i<N;i++) lin+=AnsiString(a[i])+" "; txt+=lin+"\r\n";
            cnt++;
            }
        // nested for step lequeal
        for (i=N;;)
            {
            i--; a[i]++; s++;
            for (j=i+1;j<N;j++) { s+=a[i]-a[j]; a[j]=a[i]; }
            if ((s<=sum)&&(a[i]<=max)) break;
            if (!i) { _loop=false; break; }
            s+=max-a[i]; a[i]=max;
            }
        }

    txt+=AnsiString(cnt)+" subsets found\r\n";
    return txt;
    }
//---------------------------------------------------------------------------

Now the increment works like this:

1,1,1,1
1,1,1,2
1,1,1,3
1,1,1,4
1,1,2,2
1,1,2,3
1,1,2,4
1,1,3,3
...

The speed is now awesome just few 5.4 [ms] for subset_sum(1,4,100,32);

here the result:

1 1 1 1 1 1 1 1 1 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 1 2 2 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 1 2 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 1 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 2 2 2 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 2 2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 1 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 2 2 2 2 2 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 2 2 2 2 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 2 2 2 2 2 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 2 2 2 2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 2 2 2 2 2 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 2 2 2 2 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 2 2 2 2 2 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 2 2 2 2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 
1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 2 2 2 2 2 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 2 2 2 2 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 
1 1 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 
1 1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 
1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 2 2 2 2 2 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 
1 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 
1 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 
1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 
1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 
2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 
2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 
2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 
2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 
2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 
80 subsets found
Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • I like how this gets rid of the array sum function, but there is no mechanism to ensure that the combination was already tried, so it's still really expensive. For example, this tries `1,2,2,4` and then towards the end checks `4,2,2,1`. (that is, if I translated it correctly: http://jsbin.com/potilofewo/edit?js,console) – Matt K Jul 08 '15 at 13:46
  • @MattK hmm did not occur this to me ... may be some sorting of `a[i]` would be a good solution so for any permutation would `a[i]<=a[i-1]` for any `i` if not ignore iteration ... that would speed things up a lot right now i ignore only if sum is above total – Spektre Jul 08 '15 at 14:07
  • @MattK have tried to encode my previous comment see [edit1] for result – Spektre Jul 08 '15 at 14:37
  • question updated to include results for your code. I'll probably add an upper bound before I ship, but it sure beats mine, thanks! – Matt K Jul 08 '15 at 16:04
1

It might look complicated but once you understand why arranging things in ascending order saves you from repetitions, it's very easy :)

I tried to go through your solution but could not understand it completely.

I will start with a recursive, I know you don't want a recursive solution but this one is a dynamic programming recursion, so real easy to convert it to iterative solution.

You dint give name to total, so say that you have to distribute n chocolates among k gift boxes so number of gift boxes = k & total = n and for the sake of simplicity arr= {min,min+1, .....max} is an array.

Now the key to avoid repetitions is distributing chocolates in a ascending order (descending also works). So you 7 chocolates and I put 2 chocolate in the first box, I will put at least 2 in the second box. WHY? this helps in avoiding repetitions.

         you will have to use base cases like n>0 , number of items < max           

         now onwards TCL = totalChocholatesLeft & TBL  = totalBinsLeft

         So S(TCL,TBL) =  S(TCL-TBL,TBL) + S(TCL,TBL-1);

         you have to call the above expression starting with S(n-(k*min), k)

         Why? because all boxes need at least one item so first put `min` each box. 
         Now you are left with only n-(k*min) chocolates.

That's all! that's the DP recursion.

How does it work?

        So in order to remove repetitions we are maintaning the ascending order.
        What is the easiest way to maintain the ascending order ? 

If you put 1 chocolate in the ith box, put 1 in all boxes in front of it i+1, i++2 .....k. So after keeping chocolate in a gift box, you have two choices :

Either you want to continue with current box :

                S(TCL-TBL,TBL) covers this

or to move the next box just never consider this box again

                S(TCL,TBL-1) covers this.

If you want to make it easier to maintain the max constraint, you can pass a List<Integer> which represents the number of elements in each bin. So before calling the recursion you have to increase the number of elements in each bin by 1. Which would make the TC : O(N^2K)

This is a working Code : I just wrote recursive functions you can just use use a output[n][k] array and convert it to DP, where ever there is a function call(countWays(x,y)) just check if countWays[x][y] is -1 then only call the function recursively else just return the value

    static int totalWays = 0;
    static int totalbins=32,bins=32;
    static int min=1,max=4;
    static int[][] countWays;
    public static void main(String[] args) throws IOException {
        int[] chocs = new int[bins];
        int total = 100;
        for(int i=0;i<bins;i++){
            chocs[i] =min;
        }
        countWays= new int[total+1][bins+1];

        for(int i=1;i<total+1;i++){
            for(int j=1;j<bins+1;j++){
                countWays[i][j]= -1;
            }
        }
        total = total - (min*bins);
        countWays[total][bins] =countWays(total,bins,chocs);
        System.out.println("Total ways:" + totalWays);
        System.out.println("Total ways:" + countWays[total][bins]);

    }

    private static int countWays(int total, int binsLeft, int[] chocs) {
        if(binsLeft == 1){
            chocs[totalbins-1]= chocs[totalbins-1] +total;
            if(chocs[totalbins-1] <= max) {
                doStuff(chocs);
                return 1;
            }
            countWays[total][1]=0;
            return 0;
        }
        if(total == 0 ){
            if(chocs[totalbins-binsLeft] <= max) {
                doStuff(chocs);
                return 1;
            }else{
                return 0;
            }
        }

        int binsCovered =0;
        int x=0,y=0;
        if(total >= binsLeft) {
            int[] newArray = new int[totalbins];
            for (int i = 0; i < totalbins; i++) {
                newArray[i] = chocs[i];
            }
            while (binsCovered < binsLeft) {
                newArray[totalbins - binsCovered - 1]++;
                binsCovered++;
            }
            x = countWays(total - binsLeft, binsLeft, newArray);

        }
        y = countWays(total, binsLeft-1, chocs);
        countWays[total][binsLeft] = x+y;
        return countWays[total][binsLeft];
    }

    public static void doStuff(int[] chocs) {
        totalWays++;
//        for(int i=0;i<totalbins;i++)
//        {
//           // System.out.print(chocs[i] + " ");
//        }
//        //System.out.println();
    }
Karthik
  • 4,950
  • 6
  • 35
  • 65
  • I'm having a hard time visualizing this, but I think it follows the same concept that I've already employed, but please correct me if I'm wrong. For example, `k=4, n=9, minChocolatesPerBox = 1, maxChocolatesPerBox = 4`. If my current recursion is at `1,2,4,4`, recursion @ idx 4 would end, recursion @ idx 3 would end & my recursion @ idx 2 would increment the 2 to 3. This would give me `1,3,3,3` because the following chocolate box must be >= the current. If I'm way off mark, please show me an example list of trial iterations so I can get the pattern. Cheers – Matt K Jul 08 '15 at 14:04
  • after reaching point `1,2,4,4` that means you are at 4th box, if you can't put any more chocolates. You will just terminate , you will never go back to `2` and increment it. `1,3,3,3` comes from a different way when you are at `1,2,2,2` and you are considering second box If you choose to continue with second box, it will become `1,3,3,3` but if you want to leave behind second box then your configuration is still `1,2,2,2` but your box changes to 3. In the next recursion it becomes `1,2,3,3` and later `1,2,4,4` – Karthik Jul 08 '15 at 14:15
  • Once leave a box , you will never go back to it. This is the heart of the problem which saves you from getting a repetitive order. – Karthik Jul 08 '15 at 14:17
  • took me about 30 mins of life in the debugger to understand what this was doing, but I I finally understand it, thanks! I updated my question to include a JS translation for comparison. Unfortunately, the recursion is about 100x slower than the other one. If i screwed something up in the translation to make it slow feel free to let me know. Cheers – Matt K Jul 08 '15 at 16:06
  • @MattK No, see my Answer, Just before the Code (The part which I wrote in bold) : You should use a 2D array to store the calculated results so that you don't need to calculate them again and again.Recursion repeatedly calculates the same results again and again that's why it is slow. Just try to do it, then it runs in O(KN) which is lot faster. I will write it and update the answer once reach home. – Karthik Jul 08 '15 at 16:23
  • Updated the code & included a check in the array before recursing (http://jsbin.com/vosevumafi/edit?js,console) Still don't see any improvement. – Matt K Jul 08 '15 at 17:58
  • I will try it later and let you know. – Karthik Jul 08 '15 at 18:05