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 recurseis 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.