2

The Problem

I need an algorithm that does this:

Find all the unique ways to partition a given sum across 'buckets' not caring about order

I hope I was clear reasonably coherent in expressing myself.

Example

For the sum 5 and 3 buckets, what the algorithm should return is:

[5, 0, 0]
[4, 1, 0]
[3, 2, 0]
[3, 1, 1] [2, 2, 1]

Disclaimer

I'm sorry if this question might be a dupe, but I don't know exactly what these sort of problems are called. Still, I searched on Google and SO using all wordings that I could think of, but only found results for distributing in the most even way, not all unique ways.

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
  • 1
    You are missing `[2,2,1]` – Ivaylo Strandjev Mar 13 '13 at 14:35
  • @IvayloStrandjev Thanks. If you could edit my algorithm to make that fit it? – Yatharth Agarwal Mar 13 '13 at 14:40
  • possible duplicate of [List all k-tuples with entries summing to n, ignoring rotations](http://stackoverflow.com/questions/3731705/list-all-k-tuples-with-entries-summing-to-n-ignoring-rotations) – mbeckish Mar 13 '13 at 14:42
  • Search for "integer partition" to find more dupes. – mbeckish Mar 13 '13 at 14:42
  • @mbeckish Uh..., could you give a link to a dupe? In the question that you currently link, the order matters (but rotations don't) and here both do. Therefore the questions are and should be separate. – Yatharth Agarwal Mar 14 '13 at 09:31
  • @YatharthROCK - I think you mean order DOESN'T matter for you, correct? For example, you wouldn't list [5,0,0], [0,5,0], and [0,0,5] as separate partitions in your example. – mbeckish Mar 14 '13 at 12:38
  • @YatharthROCK - Here's [another example](http://stackoverflow.com/q/11019574/21727). – mbeckish Mar 14 '13 at 12:54
  • @mbeckish Yes, I meant none mattered (typo). And I accept, the link you provided does cover the same thing. I still think both the questions should be merged as this is more general and that specific to C++. but that's up to you and the community. I'm sorry if I offended you BTW. – Yatharth Agarwal Mar 14 '13 at 13:37
  • @YatharthROCK - No offense taken. :) – mbeckish Mar 14 '13 at 13:43
  • @mbeckish Glad you didn't. I love So for that. BTW, do you still think the question should be closed? – Yatharth Agarwal Mar 14 '13 at 13:44
  • @YatharthROCK - I tend to think of it in terms of future people searching the site for an answer. Would we prefer SO to be like other forums, where every variation of the same question is there to be sifted through, some with good answers, some with awful answers that people thought were good, etc.? Or would we prefer only one of the posts to survive - the one with the clearest and most general statement of the problem, and having all of the best answers? Maybe the best solution is to keep all of the posts, and clearly link them all, so you can easily browse all of the answers. – mbeckish Mar 14 '13 at 13:52
  • @mbeckish You can merge the questions, right? And anyway, this problem is more general and the answers here will probably be more relevant to people. – Yatharth Agarwal Mar 14 '13 at 14:02

6 Answers6

2

Its bit easier for me to code few lines than writing a 5-page essay on algorithm. The simplest version to think of:

vector<int> ans;

void solve(int amount, int buckets, int max){
  if(amount <= 0) { printAnswer(); return;}
  if(amount > buckets * max) return; // we wont be able to fulfill this request anymore

  for(int i = max; i >= 1; i--){
    ans.push_back(i);
    solve(amount-i, buckets-1, i);
    ans.pop_back();
  } 
}

void printAnswer(){
  for(int i = 0; i < ans.size(); i++) printf("%d ", ans[i]);
  for(int i = 0; i < all_my_buckets - ans.size(); i++) printf("0 ");
  printf("\n");
}

Its also worth improving to the point where you stack your choices like solve( amount-k*i, buckets-k, i-1) - so you wont create too deep recurrence. (As far as I know the stack would be of size O(sqrt(n)) then.

Why no dynamic programming?

We dont want to find count of all those possibilities, so even if we reach the same point again, we would have to print every single number anyway, so the complexity will stay the same.

I hope it helps you a bit, feel free to ask me any question

  • Thanks. 3 questions:- 1) Could you explain the stacking choices part? 2) I assume this is Java. Could you please explain how the Vector var works? Like a list where you can pop and append on both ends? 3) What exactly do you mean by dynamic programming? [the Wikipedia page](http://en.wikipedia.org/wiki/Dynamic_programming) doesn't really help a lot... – Yatharth Agarwal Mar 14 '13 at 09:45
1

Here's something in Haskell that relies on this answer:

import Data.List (nub, sort)

parts 0 = []
parts n = nub $ map sort $ [n] : [x:xs | x <- [1..n`div`2], xs <- parts(n - x)]

partitions n buckets = 
  let p = filter (\x -> length x <= buckets) $ parts n 
  in map (\x -> if length x == buckets then x else addZeros x) p  
    where addZeros xs = xs ++ replicate (buckets - length xs) 0


OUTPUT:
*Main> partitions 5 3
[[5,0,0],[1,4,0],[1,1,3],[1,2,2],[2,3,0]]
Community
  • 1
  • 1
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • Thanks a lot for this. Just one thing: any more readable language? Pseudocode or Python would do. I amn't able to understand it. Cuold you explain some of the Haskell idioms/syntax you used here? – Yatharth Agarwal Mar 14 '13 at 09:47
  • @YatharthROCK I wish I could write it for you in Python but I am unfamiliar with it. I guess you would have to study a basic Haskell tutorial to learn it. The syntax [x,y | x <- [some list], y <- [some list]] is called a list comprehension and it creates all combinations of x and y, where x and y are taken from given lists. [something] is a list. nub removes duplicate elements from a list. sort sorts. map takes a function and applies it to all elements of a list. filter removes elements from a list according to a condition. ++ joins two lists. x:xs puts element x in list xs. hope that helps. – גלעד ברקן Mar 14 '13 at 14:54
0

A completely different method, but if you don't care about efficiency or optimization, you could always use the old "bucket-free" partition algorithms. Then, you could filter the search by checking the number of zeroes in the answers.

For example [1,1,1,1,1] would be ignored since it has more than 3 buckets, but [2,2,1,0,0] would pass.

Vesper
  • 18,599
  • 6
  • 39
  • 61
Bayko
  • 1,344
  • 2
  • 18
  • 25
0

This is called an integer partition.

Fast Integer Partition Algorithms is a comprehensive paper describing all of the fastest algorithms for performing an integer partition.

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
Tyler Durden
  • 11,156
  • 9
  • 64
  • 126
0

If there are only three buckets this wud be the simplest code.

for(int i=0;i<=5;i++){
        for(int j=0;j<=5-i&&j<=i;j++){
            if(5-i-j<=i && 5-i-j<=j)
                System.out.println("["+i+","+j+","+(5-i-j)+"]");
        }
}
Sudeep
  • 121
  • 5
  • I want an arbitrary amount of buckets. This doesn't do that. You would probably need a recursive algorithm. – Yatharth Agarwal Mar 13 '13 at 15:20
  • yes for an arbitrary buckets you would need a recursive code also using dynamic programming to avoid used combinations. – Sudeep Mar 13 '13 at 15:24
0

Just adding my approach here along with the others'. It's written in Python, so it's practically like pseudocode.

My first approach worked, but it was horribly inefficient:

def intPart(buckets, balls):
    return uniqify(_intPart(buckets, balls))

def _intPart(buckets, balls):
    solutions = []

    # base case
    if buckets == 1:
        return [[balls]]

    # recursive strategy
    for i in range(balls + 1):
        for sol in _intPart(buckets - 1, balls - i):
            cur = [i]
            cur.extend(sol)
            solutions.append(cur)

    return solutions

def uniqify(seq):
    seen = set()
    sort = [list(reversed(sorted(elem))) for elem in seq]
    return [elem for elem in sort if str(elem) not in seen and not seen.add(str(elem))]

Here's my reworked solution. It completely avoids the need to 'uniquify' it by the tracking the balls in the previous bucket using the max_ variable. This sorts the lists and prevents any dupes:

def intPart(buckets, balls, max_ = None):
    # init vars
    sols = []
    if max_ is None:
        max_ = balls
    min_ = max(0, balls - max_)

    # assert stuff
    assert buckets >= 1
    assert balls >= 0

    # base cases
    if (buckets == 1):
        if balls <= max_:
            sols.append([balls])
    elif balls == 0:
        sol = [0] * buckets
        sols.append(sol)

    # recursive strategy
    else:
        for there in range(min_, balls + 1):
            here = balls - there
            ways = intPart(buckets - 1, there, here)
            for way in ways:
                sol = [here]
                sol.extend(way)
                sols.append(sol)

    return sols

Just for comprehensiveness, here's another answer stolen from MJD written in Perl:

#!/usr/bin/perl

sub part {
  my ($n, $b, $min) = @_;
  $min = 0 unless defined $min;

  # base case
  if ($b == 0) {
    if ($n == 0) { return ([]) }
    else         { return ()   }
  }

  my @partitions;
  for my $first ($min .. $n) {
    my @sub_partitions = part($n - $first, $b-1, $first);
    for my $sp (@sub_partitions) {
      push @partitions, [$first, @$sp];
    }
  }
  return @partitions;
}
Community
  • 1
  • 1
Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53