(Drafted before I saw your clarifying reply to Mark T.,
so parts of this are redundant, but I can’t resist leaving in
the Tom Lehrer reference!)
The code below appears (I only tested it very briefly) to generate
what I’d call a type of (multi) combinations (rather than permutations),
but it includes some combinations
that from your examples you seemed to want to exclude?
(Well, actually, maybe it does what you want.
Where it is on the brute force … slick … clever range
is a question for others. Assuming it really works!)
(Incidentally, Bill K.'s “found” Logo example:
Sample Logo program
is doing something a bit different: it doesn’t allow “null” selections,
hence the “allow_null” parameter option in the code below.)
So for now I’m with Rick DeNatale (and Mark T.?): I don’t
understand the pattern,
so I can’t see what a generic solution (slick or brute force) might be.
From your reply to Rick DeNatale, I assume that you don’t want
%w( android )
in the generated combinations: is my assumption correct?
If it is, can you explain why/how your “generic” pattern excludes %w(
android ),
or maybe give a larger example of the combinations you want generated.
For example, what are the “allowed” combinations from:
sets = [
%w( android hero ),
%w( insane clown posse ),
%w( phenomenauts ),
%w( infinitely differential riemannian manifolds ),
]
*** incidentally, can someone explain to me why Ruby isn’t
raising an exception from that last “,” after “manifolds )” ?
I’d have expected [ 1, 2, ] to raise an exception, but in IRB:
[ 1, 2, ] #=> [1, 2]
****** code
def multi_combinations( sets, allow_null = true )
siz = sets.size - 1
select_v = Array.new( siz + 1, 0 )
combinations = []
while true do
comb = [] ; n = -1
sets.each do | subset | n += 1
if ( nn = select_v[ n ] ) < subset.size then
comb << subset[ nn ]
end
end
combinations << comb ## or yield comb
p comb ## test bit in case of infinite loop!
qq_finished = false
siz.downto(0) do | ii |
case ( select_v[ ii ] += 1 ) <=> sets[ ii ].size
when -1 then
break
when 0 then
break if allow_null
end
select_v[ ii ] = 0
qq_finished = ( ii == 0 )
end
break if qq_finished
end
combinations
end
sets = [
%w( android hero ),
%w( insane clown posse ),
%w( phenomenauts ),
]
symbols used for single letters to give less visual clutter
sets = [ [ :a, :h ], [ :i, :c, ], [ :f ] ]
puts
puts ‘** wanted sequence:’
puts ‘[:a, :i, :f]’
puts ‘[:a, :i]’
puts ‘[:a, :c]’
puts ‘[:a, :p]’
puts ‘[:h, :i, :f]’
puts ‘[]’
puts
puts ‘** generated sequence:’
mc = multi_combinations( sets )
mc.each_with_index { | s, i | puts ‘mc[’ + i.to_s + '] == ’ +
s.inspect }
choices = [ [ :small, :medium, :large ],
[ :vanilla, :ultra_chocolate, :lychee, :rum_raisin, :ginger
],
[ :cone, :cup ]
]
puts
puts ‘** generated choices:’
mc = multi_combinations( choices, false )
mc.each_with_index { | s, i | puts ‘mc[’ + i.to_s + '] == ’ +
s.inspect }
puts
puts ‘** another’
multi_combinations( [ [1, 2], [ 10 ], [100, 200, 300] ] )