Calculating Combinations The Cool Way

I recently had to calculate all possible combinations of a set. I needed to calculate combinations of 1..N size, where N is the size of the original set of things. Order inside of the resulting combinations did not matter to me, as I am treating the combinations as true sets.

For example, given the set [A,B,C], I needed to calculate the following combinations:


  • []

  • [A]

  • [B]

  • [C]

  • [AB]

  • [AC]

  • [BC]

  • [ABC]



It dawned on me that a cool way to generate the combinations was to treat the sets (the original set and the resulting combination sets) as bit strings. If the bit corresponding to the member is on, I include the member in the combination.

To explain, I start with the set [A,B,C]. I create a number that has three bits, all on, one for each member of the set. I therefore have the binary number 111 matching [A,B,C]. 111 happens to be 7 in decimal, which is one less than the total number of combinations I require.

Starting with zero, I loop up and including seven (for a total of eight iterations, once for each combo that I want). I convert each iteration count to a binary string, which will give me which bits are on for this combination.

For example, here's the ruby code:



original_set = [:A, :B, :C]
combinations = []

def create_combo(bit_string, original_set)
combo = []
bit_string.split(//).each_with_index do |bit, i|
combo << original_set[i] if bit == "1"
end
combo
end

(2**original_set.size).times do |i|
bit_string = sprintf("%03b", i)
combinations << create_combo(bit_string, original_set)
end

require 'pp'
pp combinations



This will print out:

[[], [:C], [:B], [:B, :C], [:A], [:A, :C], [:A, :B], [:A, :B, :C]]

Neat, huh?

I'm sure you can speed this up by checking each bit in the iteration count instead of first converting to a bit string.
4 comments

Popular posts from this blog

Converting Array to List in Scala

I ported a JavaScript app to Dart. Here's what I learned.

Minification is not enough, you need tree shaking