Generating unique sorted partitions in Ruby - ruby

I'm trying to generate the set of sequences as shown below, not in any particularly order, but here its shown as a descending sequence. Note that each sequence also descends as I'm interested in combinations, not permutations. I'd like to store each sequence as an array..or the set of sequences as an array of arrays more preferably, but first things first.
6
5 1
4 2
4 1 1
3 3
3 2 1
3 1 1 1
2 2 2
2 2 1 1
2 1 1 1 1
1 1 1 1 1 1
Right now I am simply focusing on generating these sets and I'm trying to do it recursively. Essentially..these are all the sequences of numbers when combines will give some total..in this case 6. But notice how when the first number is 3, the set of numbers which follows is simply the set of sequences that gives a total of 3. In other words 6(target total) - 3 (first number) = 3 (set of sequences that give total of 3). Thus, should be able to do this recursively.
I tried to code as follows (and yes, this is my first language and yes, I've only been studying for about a week so I'm sure its all screwed up) but so far no luck. I think if I can just get the very core of the recursion working and put the values of all the objects to the screen so I can trace it line by line, I think I can move ahead, but between the logic and the syntax, I'm at a stand still.
My logic is:
define a method that passes 'count' representing the total being targeted.
create an array which is going to hold a given sequence of values
create an index which represents the position in the array (ignoring zero position).
define 'delta' and initialize it to the value of 'count' and have it represent the remaining target sum of the rest of the array. (since there is nothing in the array initially, the delta is the same as the count.)
Then, cycle through the possibilities for the next(first) value of the sequence starting from 1 and ending, obviously, with the maximum possible, which is the value of 'count' itself. Determine the new delta for each value in the cycle.
If the delta is 0, you are done otherwise determine this new sequence which will give this new delta. Likely need to append the new sequence to the current sequence as well.
i=0
def seq(count)
cvc=Array.new # array to hold the number values
i=i+1 # position index for the array
puts 'i is ' + i.to_s
delta=count
puts ' delta is ' + delta.to_s
for value in 1..delta do # value represents the number value
cvc[i]=value
puts 'cvc[i] is ' + cvc[i].to_s
delta = delta-cvc.sum
puts 'new delta is '+ delta.to_s
if delta >1 then count=delta
seq(count)
end
end
end

Here's a solution:
def expand(n, max = n)
return [[]] if n == 0
[max, n].min.downto(1).flat_map do |i|
expand(n-i, i).map{|rest| [i, *rest]}
end
end
expand(6) # => [[6], [5, 1], [4, 2], [4, 1, 1], [3, 3], [3, 2, 1], [3, 1, 1, 1], [2, 2, 2], [2, 2, 1, 1], [2, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]

Related

Problems with the Iterator/Conditional Section of my Data Structure Manipulation Code - Ruby

I am stuck on the iterator/conditional section of my code, it does not complete the iteration as expected.
The routine accepts a random non negative integer and must return a rearranged integer where the leftmost digit is the greatest digit of the input integer and the rightmost digit is the smallest.
The code is as follows:
def descending_order(n)
new = n.digits
result=[]
for i in new
if i=new.max() then new.delete(i) && result.push(i)
end
end
return result.join().to_i
end
The sample input is as follows:
descending_order(6022563)
A sample of the erroneus result I get is as follows:
descending_order(6022563) should equal 6653220 - Expected: 6653220, instead got: 653
This doesn’t address WHY you’re having a problem with your code, but here’s an alternative solution:
def descending_order(n)
n.digits.sort.reverse.join.to_i
end
descending_order(6022563)
#=> 6653220
If you want to figure out what's going in your code, one of the best ways to do that, is to simply execute it step-by-step with pen and paper, keeping track of the current value of all variables in your code. So, let's do that.
However, before we start stepping through the code, let's first see if we can simplify it without changing how it works.
Firstly, the return is redundant. In Ruby, the return value of a block of code is the value of the last expression that was evaluated in that block. So, we can just remove the return.
Secondly, for … in is equivalent (not exactly, but the differences don't matter in this particular case) to #each, so your code could also be written like this:
def descending_order(n)
new = n.digits
result = []
new.each do |i|
if i = new.max
new.delete(i) && result.push(i)
end
end
result.join.to_i
end
Next, let's look at this bit:
new.delete(i) && result.push(i)
This will delete all occurrences of i from new, and also test whether the return value of new.delete(i) is trueish. If and only if the value is trueish, result.push(i) will be executed.
However, new.delete(i) will only return a falseish value (namely nil) if the value i was not found in new. But, we just assigned i to be the maximum value in new, so we know it always exists and therefore, new.delete(i) will always return i and never nil. This means result.push(i) will always be executed and the conditional is not doing anything, we can just remove it:
new.delete(i)
result.push(i)
Now, let's look at the conditional:
if i = new.max
This assigns the maximum value of the new array to i and also tests whether that value is trueish. In other words, it is equivalent to
i = new.max
if i
In Ruby, the only two values that are falseish are false and nil, so the only way this conditional can fail is if the maximum value of the new array is either false or nil. Since we know that we created the array from the digits of a number, we know that it only contains integers, not nil nor false. Enumerable#max also returns nil if the array is empty, so, the only way this can fail is if the array is empty. In other words, it is equivalent to
i = new.max
if !new.empty?
or
i = new.max
unless new.empty?
However, we also know that we are in the middle of iterating over new, so it cannot possibly be empty! If it were empty, the iteration would be executed 0 times, i.e. we would not be hitting this conditional at all.
Therefore, the conditional will always be true and can just be removed:
def descending_order(n)
new = n.digits
result = []
new.each do |i|
i = new.max
new.delete(i)
result.push(i)
end
result.join.to_i
end
Lastly, let's look at the iteration variable i. It will get assigned the current element of the iteration, but we immediately re-assign it on the first line of the block without ever looking at its value. So, it is actually not used as an iteration variable at all, it is simply a local variable and we can remove it from the block:
def descending_order(n)
new = n.digits
result = []
new.each do
i = new.max
new.delete(i)
result.push(i)
end
result.join.to_i
end
With this simplified code in place, we are now going to look at each step of each iteration of the loop separately.
We start off with the following (let's imagine we are just before the start of the first iteration):
Variable
Value
new
[3, 6, 5, 2, 2, 0, 6]
result
[]
i
But, there is actually another, hidden, variable as well: the pointer to the current index. Remember, we are in the middle of iterating over new and each must internally somehow keep track of where we are. So, even though we don't know how each works internally, we can assume that it needs to somehow, somewhere, remember where we are. So, we have another piece of state to keep track of: the current index of the iteration.
Variable
Value
new
[3, 6, 5, 2, 2, 0, 6]
result
[]
i
index
Alright, let's look at the first step of the first iteration, which is
i = new.max
Variable
Value
new
[3, 6, 5, 2, 2, 0, 6]
result
[]
i
6
index
0: [→ 3 ←, 6, 5, 2, 2, 0, 6]
The next step is
new.delete(i)
which deletes all occurrences of i from new, i.e. it deletes all occurrences of 6 from [3, 6, 5, 2, 2, 0, 6]. This leaves us with [3, 5, 2, 2, 0], but what is also important is that each doesn't know what we are doing, that we are mutating the array. Therefore, the pointer will still stay at position 0, it will not move.
Variable
Value
new
[3, 5, 2, 2, 0]
result
[]
i
6
index
0: [→ 3 ←, 5, 2, 2, 0]
The next step is
result.push(i)
which appends i to the end of the result array:
Variable
Value
new
[3, 5, 2, 2, 0]
result
[6]
i
6
index
0: [→ 3 ←, 5, 2, 2, 0]
And that's it for the first iteration!
Let's look now at the second iteration. The first thing that is going to happen is that each internally increments its counter or moves its pointer, or however each is implemented internally. Again, we don't know how each is implemented internally, but logic dictates that it must somehow keep track of where we are in the iteration, and we can also assume that before the next iteration, it will somehow need to move this.
So, at the beginning of the iteration, the situation now looks like this:
Variable
Value
new
[3, 5, 2, 2, 0]
result
[6]
i
index
1: [3, → 5 ←, 2, 2, 0]
Alright, let's look at the first step of the second iteration:
i = new.max
We again assign i to the maximum value of new, which is now 5:
Variable
Value
new
[3, 5, 2, 2, 0]
result
[6]
i
5
index
1: [3, → 5 ←, 2, 2, 0]
Next step is
new.delete(i)
which deletes all occurrences of 5 from [3, 5, 2, 2, 0], which leaves us with [3, 2, 2, 0]. But remember what we also said above: each doesn't know what we are doing, that we are mutating the array. Therefore, the pointer will still stay at position 1, it will not move. But the item that is at position 1 (which is the number 5) will be deleted! That means, all elements after the 5, i.e. all elements after index 1 now get shifted one index to the left, and that means the index pointer is still pointing at the same index but there is now a different element at that index:
Variable
Value
new
[3, 2, 2, 0]
result
[6]
i
5
index
1: [3, → 2 ←, 2, 0]
The root cause of this problem is that we are mutating new at the same time that we are iterating over it. This is a big no-no. You should never mutate a data structure while you are processing it. Or, at the very least, you need to be very careful that the mutations you do perform do not cause you to incorrectly process or skip parts of it.
Next step is
result.push(i)
which means we push 5 to the end of result:
Variable
Value
new
[3, 2, 2, 0]
result
[6, 5]
i
5
index
1: [3, → 2 ←, 2, 0]
Alright, let's get to the next iteration: the pointer is again pushed forward to the next element, which leaves us with this picture:
Variable
Value
new
[3, 2, 2, 0]
result
[6, 5]
i
index
2: [3, 2, → 2 ←, 0]
First step:
i = new.max
Variable
Value
new
[3, 2, 2, 0]
result
[6, 5]
i
3
index
2: [3, 2, → 2 ←, 0]
Next step:
new.delete(i)
Variable
Value
new
[2, 2, 0]
result
[6, 5]
i
3
index
2: [2, 2, → 0 ←]
Next step:
result.push(i)
Variable
Value
new
[2, 2, 0]
result
[6, 5, 3]
i
3
index
2: [2, 2, → 0 ←]
As you can see, the iteration pointer is now at the end of the array, so there will be no next iteration. Therefore, the final value of the result array is [6, 5, 3], which we now join into a string and convert to an integer, so the final result is 653.
Fundamentally, there are four problems with your code:
Presumably, the if i = new.max combined assignment / conditional which always clobbers the value of i is a typo, and you meant to write if i == new.max.
You are mutating the new array while at the same time you are iterating over it.
You are deleting every occurrence of i from new, but you are only adding one occurrence to the result.
Your logic is wrong: if the current element is not the maximum element, you skip over it and ignore it completely. But if the current element is not the maximum element, that only means that it should appear in the output later, not that it should not appear at all.
If you only fix #1, i.e. you replace i = new.max with i == new.max and change nothing else about your code, the result will be 6. If you only fix #2, i.e. you replace for i in new with for i in new.dup (thus duplicating the new array and iterating over the copy so that mutating the new array itself does not influence the iteration), the result will be 65320. If you fix both #1 and #2, the result will be 65.
I encourage you to take pen and paper and trace all those three variants the same way we did above and fully understand what is going on.

Lucas Sequence in Ruby

The Lucas Sequence is a sequence of numbers. The first number of the sequence is 2. The second number of the Lucas Sequence is 1. To generate the next number of the sequence, we add up the previous two numbers. For example, the first six numbers of the sequence are: 2, 1, 3, 4, 7, 11, ...
Write a method lucasSequence that accepts a number representing a length as an arg. The method should return an array containing the Lucas Sequence up to the given length. Solve this recursively.
def lucas_sequence(length)
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
seq = lucas_sequence(length - 1)
next_el = seq[-1] + seq[-2]
seq << next_el
seq
end
p lucas_sequence(0) # => []
p lucas_sequence(1) # => [2]
p lucas_sequence(2) # => [2, 1]
p lucas_sequence(3) # => [2, 1, 3]
p lucas_sequence(6) # => [2, 1, 3, 4, 7, 11]
p lucas_sequence(8) # => [2, 1, 3, 4, 7, 11, 18, 29]
**I'm having a hard time understanding the recursion logic behind this. Can someone explain how the computer is solving this?
Does the computer read the length and then add up from [2,1] until it reaches its length? If so, how does it continuously count down? **
Recursion is the programming equivalent of mathematical induction. Given a series, assume that the problem is solved for the previous member of the series and provide the rule for generating this member.
So, consider just these lines:
def lucas_sequence(length)
seq = lucas_sequence(length - 1) # <1>
next_el = seq[-1] + seq[-2] # <2>
seq << next_el # <3>
seq # <4>
end
That says:
You want to know the lucas sequence of a certain length (length). Fine, first tell me the previous lucas sequence, the sequence that is one unit shorter than this (length-1). (That is the recursion: the lucas_sequence method, itself, calls the lucas_sequence method, but with a reduced length value.)
Add up the last two members of that shorter sequence...
...and append the sum to that shorter sequence...
...and the result is this sequence, the one you asked for.
And that's basically all there is to it! The only problem is that there is no place to start. We assume that for the seq of length 4, we have solved 3 already, which we get by assuming that we have solved 2 already, which we get by assuming we have solve 1 already... But we haven't actually solved any of those!
So we begin by backstopping the most degenerate cases:
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
Now the problem is solved if length is 0, 1, or 2, because we just give those answers directly. Okay, so if length is 3, we solve with reference to 2, which is known. Okay, if length is 4, we solve with reference to 3, and I just told you how to do that. Okay, if length is 5, we solve with reference to 4, and I just told you how to do that. And so on, for any length you care to give me.
So it is essentially a modified Fibonacci sequence. Best way to solve most structured sequences is with an Enumerator e.g.
lucas = Enumerator.new do |y|
a,b = 2,1
loop do
y << a
a, b = b, a + b
end
end
Then
lucas.first(10)
#=> [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
First we create a new Enumerator and then assign a and b to your starting values (2 and 1 respectively).
To generate the sequence we use a loop which will lazily yield the values to the yielder (y).
Here we push in a then we assign a to bs value and bs value to a + b in parallel to avoid overwriting a before the addition of a + b.

Creating a hash of an array of 3 numbers with the following structure?

Consider the following array:
0 1 0
1 1 1
2 2 0
3 3 1
4 5 1
I'm trying to find the fastest way to hash the array such that other permutations of the 3rd column will give the same hash.
For example the following should get the same hash as the one above:
0 1 1
1 1 0
2 2 1
3 3 0
4 5 0
The first column will always be starting at 0 and increasing, an index.
The second can be essentially any value.
The third column represents categories. As long as the set of categories, contains the same index, value pairs. The hash should be the same.
E.g. from the first example:
Category 1: [1, 1] [3, 3] [4, 5]
Category 0: [0, 1] [2, 2]
E.g. from the second example:
Category 1: [0, 1] [2, 2]
Category 0: [1, 1] [3, 3] [4, 5]
Because the set of categories are the same, they should produce the same hash.
I'm trying to write a hashcode() function that essentially does this in the fastest way as runtime is a major concern.
I'm currently just creating a stack for each category by adding the integers of the pairs in order one by one. Then adding the stack to a set, and getting the hashcode of the set.
E.g. Stack1 = [1, 1, 3, 3, 4, 5]
E.g. Stack0 = [0, 1, 2, 2]
Them I add the stacks to a set and get that hashcode.
Simply make the sum of the 3rd column part of your hash-value. The sum is the same for all permutations (also product or xor would work)
Or all of them sum, xor and product that reduces the number of collisions for the 3rd column where the columns are not a permutation.
a f k
b g l
c h m
d i n
e j o
a hashCode could be
abcdefghijxyz
where:
x=k+l+m+n+o
y=k*l*m*n*o
z=k^l^m^n^o

Adding the number to the according index it is positioned in

def incrementer(num)
num.map.with_index(1) do |row, index|
if row > 9
row.to_s.reverse.chop.to_i
else
index + row
end
end
end
Hi,
I have a method which adds the number to the according index it is positioned in. However I have two rules:
The index starts at (1)
If the number is a multiple, remove the first integer off the end of it. 12 would become 2 for example.
The problem is I am unsure how to include an 'if' statement inside a block and I believe I am doing it wrong. I know there is a much better way to write this statement but I am finding it hard.
Example:
incrementer([4,6,9,1,3]) => [5,8,2,5,8]
- 9 + 3 (position of 9 in array) = 12
- Only its last digit 2 should be returned
Fixing your code
Use map followed by with_index(1), the argument offsetting the initial index by +1.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.digits.first
else
row
end
end
end
incrementer [3, 2, 4, 10] #=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3] #=> [5, 8, 2, 5, 8]
Negative numbers
Numbers can be negative and for which we can use abs.digits but it may be better to use Integer#remainder.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
case row
when -9..9
row
else
row.remainder 10
end
end
end
incrementer [3, 2, 4, 10]
#=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3]
#=> [5, 8, 2, 5,8]
incrementer [3, 2, 4,-17]
#=> [4, 4, 7, -3]
incrementer [4, 6, -22, 1, 3]
#=> [5, 8, -9, 5, 8]
Why not use % instead of Integer#remainder?
a % b returns a modulo b which is not the same thing as the remainder. This has consequences for negative numbers:
-12 % 10 #=> 8 not the last digit
So we need to use Integer#remainder, so:
-12.remainder 10 #=> -2
Which is what we need, i.e. the last digit (parity included). It can be noted in other languages such as JavaScript, a % b returns the remainder.
As noted by #moveson the above code can be refactored to:
num.map.with_index(1) { |row, index| (index + row).remainder 10 }
The #digits method requires Rails or ActiveSupport (or Ruby 2.4+), and it's not necessary. Here is a pure Ruby solution that works with any Ruby version:
def incrementer(array)
array.map.with_index(1) { |integer, index| (integer + index) % 10 }
end
This code above says: For each element of the array, add its index (starting from 1), divide the sum by 10, and return the remainder.
The % (modulo) operator divides the number before it by the number after it and returns the remainder. For example, 22 % 7 returns 1. It's an extremely useful tool and can often help avoid the use of conditionals entirely, such as in your example. Using %, you can get the last digit of a number n (whether or not n is greater than 9) by simply taking n % 10.
Now you can do:
>> incrementer([3,2,4,10])
=> [4, 4, 7, 4]
>> incrementer([4,6,9,1,3])
=> [5, 8, 2, 5, 8]
You've got two separate problems. The first problem is your use of each_with_index. Ruby functions return the value of the last expression they execute, so if you look at your function:
def incrementer(num)
num.each_with_index do |row, index|
# do some stuff
end
end
It essentially calls each_with_index and returns the value. The issue here is that each_with_index iterates over an array and then returns the original array. What you want to do is change each item in the array and return a new array with the new values. For this, you can use map:
def incrementer(num)
num.map.with_index(1) do |row, index|
# do some stuff
end
end
In this case, you can even conveniently pass in the parameter 1 to tell the iterator to start at index 1.
Now the second problem is that your if-else-statement either iterates a number or wraps it around. But what you actually want to do is iterate a number and wrap it around if it's bigger than 9. You can do that like so:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.to_s.reverse.chop.to_i
else
row
end
end
end
What we're doing here is iterating the number first, saving it into row and then checking to see if it's over 9. If it is, we return the last digit; otherwise, we just return the value row.
This code will do what you want, but let's make one more change for the sake of clarity. This code:
row.to_s.reverse.chop.to_i
Is a little bit confusing. Ruby 2.4 has a convenient method for getting at the digits of an integer:
row.digits.first
This is easy to read, but it's a bit slow since it turns an integer into an array of integers. A better way is to use modulus % to get the remainder of your number, divided by 10. This is fast and easy to read for most programmers, since it's a common trick.
row % 10
Putting it all together, you get:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row % 10
else
row
end
end
end
Special thanks to (#sagarpandya84) and (#moveson) for allowing me to build on their answers.

Loop through different sets of unique permutations

I'm having a hard time getting started to layout code for this problem.
I have a fixed amount of random numbers, in this case 8 numbers.
R[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
That are going to be placed in 3 sets of numbers, with the only constraint that each set contain minimum one value, and each value can only be used once. Edit: all 8 numbers should be used
For example:
R1[] = { 1, 4 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
I need to loop through all possible combinations of a set R1, R2, R3. Order is not important, so if the above example happened, I don't need
R1[] = { 4, 1 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
NOR
R1[] = { 2, 8, 5, 6 }
R2[] = { 7, 3 }
R3[] = { 1, 4 }
What is a good method?
I have in front of me Knuth Volume 4, Fascicle 3, Generating all Combinations and Partitions, section 7.2.1.5 Generating all set partitions (page 61 in fascicle).
First he details Algorithm H, Restricted growth strings in lexicographic order due to George Hutchinson. It looks simple, but I'm not going to dive into it just now.
On the next page under an elaboration Gray codes for set partitions he ponders:
Suppose, however, that we aren't interested in all of the partitions; we might want only the ones that have m blocks. Can we run this through the smaller collection of restricted growth strings, still changing one digit at a time?
Then he details a solution due to Frank Ruskey.
The simple solution (and certain to be correct) is to code Algorithm H filtering on partitions where m==3 and none of the partitions are the empty set (according to your stated constraints). I suspect Algorithm H runs blazingly fast, so the filtering cost will not be large.
If you're implementing this on an 8051, you might start with the Ruskey algorithm and then only filter on partitions containing the empty set.
If you're implementing this on something smaller than an 8051 and milliseconds matter, you can seed each of the three partitions with a unique element (a simple nested loop of three levels), and then augment by partitioning on the remaining five elements for m==3 using the Ruskey algorithm. You won't have to filter anything, but you do have to keep track of which five elements remain to partition.
The nice thing about filtering down from the general algorithm is that you don't have to verify any cleverness of your own, and you change your mind later about your constraints without having to revise your cleverness.
I might even work a solution later, but that's all for now.
P.S. for the Java guppies: I discovered searching on "George Hutchison restricted growth strings" a certain package ca.ubc.cs.kisynski.bell with documentation for method growthStrings() which implements the Hutchison algorithm.
Appears to be available at http://www.cs.ubc.ca/~kisynski/code/bell/
Probably not the best approach but it should work.
Determine number of combinations of three numbers which sum to 8:
1,1,6
1,2,5
1,3,4
2,2,4
2,3,3
To find the above I started with:
6,1,1 then subtracted 1 from six and added it to the next column...
5,2,1 then subtracted 1 from second column and added to next column...
5,1,2 then started again at first column...
4,2,2 carry again from second to third
4,1,3 again from first...
3,2,3 second -> third
3,1,4
knowing that less than half is 2 all combinations must have been found... but since the list isn't long we might as well go to the end.
Now sort each list of 3 from greatest to least(or vice versa)
Now sort each list of 3 relative to each other.
Copy each unique list into a list of unique lists.
We now have all the combinations which add to 8 (five lists I think).
Now consider a list in the above set
6,1,1 all the possible combinations are found by:
8 pick 6, (since we picked six there is only 2 left to pick from) 2 pick 1, 1 pick 1
which works out to 28*2*1 = 56, it is worth knowing how many possibilities there are so you can test.
n choose r (pick r elements from n total options)
n C r = n! / [(n-r)! r!]
So now you have the total number of iterations for each component of the list for the first one it is 28...
Well picking 6 items from 8 is the same as creating a list of 8 minus 2 elements, but which two elements?
Well if we remove 1,2 that leaves us with 3,4,5,6,7,8. Lets consider all groups of 2... Starting with 1,2 the next would be 1,3... so the following is read column by column.
12
13 23
14 24 34
15 25 35 45
16 26 36 46 56
17 27 37 47 57 67
18 28 38 48 58 68 78
Summing each of the above columns gives us 28. (so this only covered the first digit in the list (6,1,1) repeat the procedure for the second digit (a one) which is "2 Choose 1" So of the left over two digits from the above list we pick one of two and then for the last we pick the remaining one.
I know this is not a detailed algorithm but I hope you'll be able to get started.
Turn the problem on it's head and you'll find a straight-forward solution. You've got 8 numbers that each need to be assigned to exactly one group; The "solution" is only a solution if at least one number got assigned to each group.
The trivial implementation would involve 8 for loops and a few IF's (pseudocode):
for num1 in [1,2,3]
for num2 in [1,2,3]
for num3 in [1,2,3]
...
if ((num1==1) or (num2==1) or (num3 == 1) ... (num8 == 1)) and ((num1 == 2) or ... or (num8 == 2)) and ((num1 == 3) or ... or (num8 == 3))
Print Solution!
It may also be implemented recursively, using two arrays and a couple of functions. Much nicer and easier to debug/follow (pseudocode):
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
positions = [0, 0, 0, 0, 0, 0, 0, 0]
function HandleNumber(i) {
for position in [1,2,3] {
positions[i] = position;
if (i == LastPosition) {
// Check if valid solution (it's valid if we got numbers in all groups)
// and print solution!
}
else HandleNumber(i+1)
}
}
The third implementation would use no recursion and a little bit of backtracking. Pseudocode, again:
numbers = [1,2,3,4,5,6,7,8]
groups = [0,0,0,0,0,0,0,0]
c_pos = 0 // Current position in Numbers array; We're done when we reach -1
while (cpos != -1) {
if (groups[c_pos] == 3) {
// Back-track
groups[c_pos]=0;
c_pos=c_pos-1
}
else {
// Try the next group
groups[c_pos] = groups[c_pos] + 1
// Advance to next position OR print solution
if (c_pos == LastPostion) {
// Check for valid solution (all groups are used) and print solution!
}
else
c_pos = c_pos + 1
}
}
Generate all combinations of subsets recursively in the classic way. When you reach the point where the number of remaining elements equals the number of empty subsets, then restrict yourself to the empty subsets only.
Here's a Python implementation:
def combinations(source, n):
def combinations_helper(source, subsets, p=0, nonempty=0):
if p == len(source):
yield subsets[:]
elif len(source) - p == len(subsets) - nonempty:
empty = [subset for subset in subsets if not subset]
for subset in empty:
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+1):
yield combination
subset.pop()
else:
for subset in subsets:
newfilled = not subset
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+newfilled):
yield combination
subset.pop()
assert len(source) >= n, "Not enough items"
subsets = [[] for _ in xrange(n)]
for combination in combinations_helper(source, subsets):
yield combination
And a test:
>>> for combination in combinations(range(1, 5), 2):
... print ', '.join(map(str, combination))
...
[1, 2, 3], [4]
[1, 2, 4], [3]
[1, 2], [3, 4]
[1, 3, 4], [2]
[1, 3], [2, 4]
[1, 4], [2, 3]
[1], [2, 3, 4]
[2, 3, 4], [1]
[2, 3], [1, 4]
[2, 4], [1, 3]
[2], [1, 3, 4]
[3, 4], [1, 2]
[3], [1, 2, 4]
[4], [1, 2, 3]
>>> len(list(combinations(range(1, 9), 3)))
5796

Resources