This recursive method generates the permutations directly, as opposed to generating large numbers of permutations and discarding those that do not satisfy the specification.
Code
def all_perms(arr_size, nbr_false)
return nil if nbr_false > arr_size/2
return [true]*arr_size if nbr_false.zero?
recurse(arr_size, nbr_false, true)
end
def recurse(arr_size, nbr_false, full_arr)
last_first = arr_size + 1 - 2*nbr_false
(0..last_first).each_with_object([]) do |i,a|
pre = [true]*i << false
case nbr_false
when 1
a << pre + [true]*(arr_size-pre.size)
else
pre << true
sub_arr_size = arr_size - pre.size - (i.zero? && full_arr ? 1 : 0)
post = [true]*(arr_size-pre.size-sub_arr_size)
recurse(sub_arr_size, nbr_false-1, false).each { |aa| a << pre + aa + post }
end
end
end
Examples
arr_size = 5
nbr_false = 2
b = all_perms(arr_size, nbr_false)
#=> [[false, true, false, true, true],
# [false, true, true, false, true],
# [true, false, true, false, true],
# [true, false, true, true, false],
# [true, true, false, true, false]]
b == b.uniq
#=> true
b.any? { |a| a.each_cons(2).any? { |x,y| x == false && y == false} }
#=> false
b.any? { |a| a.first == false && a.last == false }
#=> false
arr_size = 8
nbr_false = 3
b = all_perms(arr_size, nbr_false)
#=> [[false, true, false, true, false, true, true, true],
# [false, true, false, true, true, false, true, true],
# [false, true, false, true, true, true, false, true],
# [false, true, true, false, true, false, true, true],
# [false, true, true, false, true, true, false, true],
# [false, true, true, true, false, true, false, true],
# [true, false, true, false, true, false, true, true],
# [true, false, true, false, true, true, false, true],
# [true, false, true, false, true, true, true, false],
# [true, false, true, true, false, true, false, true],
# [true, false, true, true, false, true, true, false],
# [true, false, true, true, true, false, true, false],
# [true, true, false, true, false, true, false, true],
# [true, true, false, true, false, true, true, false],
# [true, true, false, true, true, false, true, false],
# [true, true, true, false, true, false, true, false]]
b == b.uniq
#=> true
b.any? { |a| a.each_cons(2).any? { |x,y| x == false && y == false} }
#=> false
b.any? { |a| a.first == false && a.last == false }
#=> false
Notes
- The valid permutations are the same for all arrays of a given size that contain the same number of
false
elements. I therefore made the arguments of all_perms
the size of the desired array and the number of false
elements it is to contain (the other elements all being true
).
- The third argument of
recurse
, full_arr
, is a boolean that equals true
when recurse
is called from all_perms
but equals false
when recurse
is called from recurse
. This was necessary to avoid permutations that start and end with false
(the cycling condition that is to be avoided). When full_arr
is true
and i #=> 0
, the last element of the sub-array being constructed must be true
. In all other situations it may be true
or false
.
- The index
i
refers to the index of the sub-array being constructed at which the first false
is located. If, for example, arr_size #=> 4
, nbr_false #=> 2
and full_arr #=> false
, the index i
of the first false
can be 0
or 1
. It cannot be 2
, as that would require the last two elements to be false
.