intelligently generating combinations of combinations - ruby

Let's say I have a class of 30 students and want generate every possible way in which they can be partitioned into groups of 5 (order is irrelevant).
I know how to find all the combinations of students to form one group individually (http://www.merriampark.com/comb.htm). By using that iterator and some recursion, I can find PERMUTATIONS of the possible group combinations. However, order in which the groups are selected isn't relevant and I'd like to minimize my execution time. So how do I find the unique COMBINATIONS of the possible groups?
The above algorithm uses lexicographical ordering to avoid generating duplicate combinations... is there a way that I can use that idea on groups instead of on objects?
I know Ruby well and Java/Python less well. Thanks in advance for any advice!

Well, there's (30C5*25C5*20C5*15C5*10C5*5C5)/6! = 30!/(6!*5!6) = 123,378,675,083,039,376 different partitons of 30 into groups of 5, so generating them all will take some time, no matter what method you use.
In general, though, a good method to selecting such a partition is to use some ordering on the elements, and find the grouping for the highest ungrouped element, and then group the rest.
find_partition = lambda do |elts|
if elts.empty?
[[]]
else
highest = elts.pop
elts.combination(4).map do |others|
find_partition[elts - others].map { |part| part << [highest,*others] }
end.inject(:+)
end
end
find_partition[(1..30).to_a]
This way you're only generating each partition once

This is an old question, but anyway, for the record, that's how I would it in Ruby:
class Array
def groups_of_size(n)
Enumerator.new do |yielder|
if self.empty?
yielder.yield([])
else
self.drop(1).combination(n-1).map { |vs| [self.first] + vs }.each do |values|
(self - values).groups_of_size(n).each do |group|
yielder.yield([values] + group)
end
end
end
end
end
end
I use an enumerator because the output can grow very quickly, a strict output (an array for example) wouldn't be useful. A usage example:
>> pp [0, 1, 2, 3, 4, 5].groups_of_size(3).to_a
=>
[[[0, 1, 2], [3, 4, 5]],
[[0, 1, 3], [2, 4, 5]],
[[0, 1, 4], [2, 3, 5]],
[[0, 1, 5], [2, 3, 4]],
[[0, 2, 3], [1, 4, 5]],
[[0, 2, 4], [1, 3, 5]],
[[0, 2, 5], [1, 3, 4]],
[[0, 3, 4], [1, 2, 5]],
[[0, 3, 5], [1, 2, 4]],
[[0, 4, 5], [1, 2, 3]]]

You could do some post-processing on the permutations. Some pseudo-code (implement in the language of your choice...):
// We have a list of lists called 'permutations'
// combinations is an (empty) list of lists
for each permutation in permutations
{
sortedPermutation = permutation.sort()
if (! combinations.find(sortedPermutation) )
{
combinations.add(sortedPermutation);
}
}
Probably not the most efficient; I'd add the sort & compare to the code that generates the permutations personally.

One possibility would be to find all combinations to form an individual group, then go through and generate combinations that don't contain members of that individual group. Something like:
List<List<Student>> combinations=Combinations(students);
public void GenerateCombinations(int startingIndex, List<List<Student>> currentGroups, int groupsLeft)
{
if(groupsLeft==0) ProcessCombination(currentGroups);
for(int i=startingIndex; i<combinations.Count; i++)
{
if combinations[i] does not contain a student in current groups
GenerateCombinations(i+1, currentGroups + combinations[i], groupsLeft -1);
}
}
It won't be the most efficient method to go about it, but it should generate all combinations of groups. I suspect better performance could be had if you were to generate temporary lists of combinations, where in all groups that can't occur were removed, but that would be a bit more complex.
As a slight aside, there should be 142,506 combinations of 30 students to form a single group of 5. My <sarcasm> awesome </sarcasm> math skills suggest that there should be about 10^17 = 100 quadrillion combinations of groups of students (30!/((5!^6)*6!); 30! orderings of students, ordering of 6 groups of 5 does not matter, and ordering of those 6 groups doesn't matter). You might be sitting there a while waiting for this to finish.

Related

Generate random numbers with exceptions

I want to generate a pair of random numbers withing a range but also the pair must not be contained in an array of pairs I have, so you can basically think of the task as generating a random pair with exceptions. I know you can do it with a loop but I've been told it's possible with only one level of indentation. I've been searching around for something similar, so far no results. Your help would be much obliged, cheers.
Very inefficient, but expressive and short:
range = (1..3).to_a
undesired_pairs = [[1, 1], [2, 2], [3, 3]]
(range.product(range) - undesired_pairs).sample # => [1, 3]
Another solution: Create a random pair until you have a result, that is not in undesired_pairs:
undesired_pairs = [[1, 1], [2, 2], [3, 3]]
until ! undesired_pairs.include?(hit =[rand(3)+1,rand(3)+1])
end
p hit

How do I explode an internal array in Ruby if the internal count is less than a certain value?

Using Ruby 2.1, if I have an array like:
[[1,1], [2,3], [5,8], [6, 4]]
How can I convert that to an array that only has internal arrays with a count > 3?
For example, it should be:
[1, 2, 2, 2, [5,8], [6,4]]
So [5,8] and [6,4] would "pass" because their counts are > 3 but [1,1] and [2,3] would "fail" and explode out because their counts are < than 4.
EDIT
Sorry, I wasn't very clear. By "counts" I mean the second value in the internal arrays. For example, the [2,3] would have a value of 2 and a count of 3. [5,8] would have a value of 5 and a count of 8.
So if the count is > 3 then keep the original array. If the count is 3 or less, then explode the value out count number of times.
I'm pretty sure someone can come up with a better way of doing this, but:
input = [[1,1], [2,3], [5,8], [6, 4]]
input.flat_map {|val, ct| ct > 3 ? [[val, ct]] : Array.new(ct, val) }
# => [1, 2, 2, 2, [5, 8], [6, 4]]
The basic idea is that we just map the inputs (each entry) to an output (the original entry or an exploded list of values) by the count. I'm using flat_map here, but you could use the same technique with map {}.flatten(1) if you wanted. You could also use inject or each_with_object to collect the output values, which may be more straightforward but slightly less terse.
Try this:
data = [[1,1], [2,3], [5,8], [6, 4]]
results = []
data.each do |arr|
val, count = arr
if count > 3
results << arr
else
results.concat [val] * count
end
end
p results
--output:--
[1, 2, 2, 2, [5, 8], [6, 4]]
arr = [[1,1], [2,3], [5,8], [6, 4]]
arr.flat_map { |a| (a.last > 3) ? [a] : [a.first]*a.last }
#=> [1, 2, 2, 2, [5, 8], [6, 4]]
Thanks to #ChrisHeald for pointing out that flat_map is equivalent to map {}.flatten(1) (I previously had the latter) and to #7stud for telling me my original solution was incorrect, which gave me the opportunity to make my solution more interesting as well as (hopefully) correct.

Ruby split array into X groups

I need to split an array into X smaller array. I don't care about the number of elements in the smaller arrays I just need to create X arrays from a larger one. I've been doing some reading and it seems like I need a method similar to the in_groups method from rails. I am not using rails right now, just ruby.
Requiring Rails just to get that function is overkill. Just use each_slice:
team = ['alice', 'andy', 'bob', 'barry', 'chloe', 'charlie']
=> ["alice", "andy", "bob", "barry", "chloe", "charlie"]
team.each_slice(2).to_a
=> [["alice", "andy"], ["bob", "barry"], ["chloe", "charlie"]]
each_slice's parameter is the number of elements in each slice (except possibly the last slice). Since you're looking for X slices, you can do something like this:
team.each_slice(team.length/X).to_a
That's not perfect, as you'll get one extra slice if the array length is not a multiple of X, but gets you in the ballpark and you can tweak it from there depending on what exactly your needs are.
Since you say you don't care how many are in each, you could just use the length/x approach above, then check to see if you have one too many slices. If so, just join the last two slices into one jumbo-size slice. This might avoid some fussy math or floating point operations.
You can make your own method, here's a basic idea:
class Array
def my_group(x)
start = 0
size = (self.size() / Float(x)).ceil
while x > 0
yield self[start, size]
size = ((self.size() - 1 - start) / Float(x)).ceil
start += size
x -= 1
end
end
end
%w(1 2 3 4 5 6 7 8 9 10).my_group(3) {|group| p group}
# =>["1", "2", "3", "4"]
# =>["4", "5", "6"]
# =>["7", "8", "9"]
I decided to put:
require 'active_support/core_ext/array/grouping'
if x is a count of groups:
x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.in_groups(x)
=> [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
if group by x pieces:
x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.each_slice(x).to_a
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
If you need to have N groups, you can use the in_groups monkey-patch provided by ActiveSupport, as mentioned in another answer:
require 'active_support/core_ext/array/grouping'
my_array = [1,2,3,4,5]
my_array.in_groups(2)
# => [[1, 2, 3], [4, 5, nil]]
my_array.in_groups(2, false)
# => [[1, 2, 3], [4, 5]]
If you care about the number of elements in the group as opposed to the number of groups, you can use each_slice provided by Ruby core:
my_array = [1,2,3,4,5]
my_array.each_slice(2).to_a
# => [[1, 2], [3, 4], [5]]

Sorting arrays, and selecting lowest numbers?

I have created several arrays, containing multiple integers. Now i want the integers to be sorted, lowest first. Say for instance, i have this in an array: 6,6,1,2,4,4, i want it to be sorted: 1,2,4,4,6,6. Also, is there anyway i can make ruby recognize the 4 lowest values, and display them somehow? I have tried to mess around with .show, but since im quite new to programming i'm rather confused by the results i receive.
did you try this?
a = [6,6,1,2,4,4]
p a.sort
#=> [1, 2, 4, 4, 6, 6]
sort will sort in ascending order.
if you need them sorted in descending order, use sort with a block:
p a.sort {|a,b| b <=> a}
#=> [6, 6, 4, 4, 2, 1]
UPDATE: not sure how i missed the part about lowest values ...
thank you #Mladen
a.sort.take(4)
#=> [1, 2, 4, 4]

Ruby: Standard recursion patterns

One of the things I commonly get hooked up on in ruby is recursion patterns. For example, suppose I have an array, and that may contain arrays as elements to an unlimited depth. So, for example:
my_array = [1, [2, 3, [4, 5, [6, 7]]]]
I'd like to create a method which can flatten the array into [1, 2, 3, 4, 5, 6, 7].
I'm aware that .flatten would do the job, but this problem is meant as an example of recursion issues I regularly run into - and as such I'm trying to find a more reusable solution.
In short - I'm guessing there's a standard pattern for this sort of thing, but I can't come up with anything particularly elegant. Any ideas appreciated
Recursion is a method, it does not depend on the language. You write the algorithm with two kind of cases in mind: the ones that call the function again (recursion cases) and the ones that break it (base cases). For example, to do a recursive flatten in Ruby:
class Array
def deep_flatten
flat_map do |item|
if item.is_a?(Array)
item.deep_flatten
else
[item]
end
end
end
end
[[[1]], [2, 3], [4, 5, [[6]], 7]].deep_flatten
#=> [1, 2, 3, 4, 5, 6, 7]
Does this help? anyway, a useful pattern shown here is that when you are using recusion on arrays, you usually need flat_map (the functional alternative to each + concat/push).
Well, if you know a bit of C , you just have to visit the docs and click the ruby function to get the C source and it is all there..
http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-flatten
And for this case, here is a Ruby implementation
def flatten values, level=-1
flat = []
values.each do |value|
if level != 0 && value.kind_of?(Array)
flat.concat(flatten(value, level-1))
else
flat << value
end
end
flat
end
p flatten [1, [2, 3, [4, 5, [6, 7]]]]
#=> [1, 2, 3, 4, 5, 6, 7]
Here's an example of a flatten that's written in a tail recursive style.
class Array
# Monkeypatching the flatten class
def flatten(new_arr = [])
self.each do |el|
if el.is_a?(Array)
el.flatten(new_arr)
else
new_arr << el
end
end
new_arr
end
end
p flatten [1, [2, 3, [4, 5, [6, 7]]]]
#=> [1, 2, 3, 4, 5, 6, 7]
ruby
Although it looks like ruby isn't always optimized for tail recursion: Does ruby perform tail call optimization?

Resources