После точности в вопросе, вот пересмотренная версия.Я сохраняю предыдущий ответ, поскольку он также может быть полезен и использует менее сложный порядок.
# yields the possible cartesian products of [first, *rest], where the total
# of the indices that are "distributed" is exactly +nb+ and each index doesn't
# go beyong +depth+, but at least one of them is exactly +depth+
def distribute(nb, depth, reached, first, *rest)
from = [nb - rest.size * depth, 0].max
to = [first.size-1, depth, nb].min
from.upto(to) do |i|
obj = first[i]
reached ||= i == depth
if rest.empty?
yield [obj] if reached
else
distribute(nb - i, depth, reached, *rest) do |comb|
yield [obj, *comb]
end
end
end
end
def depth_first_cartesian(*arrays)
return to_enum __method__, *arrays unless block_given?
lengths = arrays.map(&:length)
total = lengths.inject(:+)
lengths.max.times do |depth|
depth.upto(arrays.size * depth) do |nb|
distribute(nb, depth, false, *arrays) {|c| yield c}
end
end
end
p depth_first_cartesian([1, 2, 3], [1, 2, 3, 4], [1, 2, 3]).to_a
# => [[1, 1, 1], [1, 1, 2], [1, 2, 1], [2, 1, 1], [1, 2, 2], [2, 1, 2], [2, 2, 1], [2, 2, 2],
# [1, 1, 3], [1, 3, 1], [3, 1, 1], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2],
# [3, 2, 1], [1, 3, 3], [2, 2, 3], [2, 3, 2], [3, 1, 3], [3, 2, 2], [3, 3, 1], [2, 3, 3],
# [3, 2, 3], [3, 3, 2], [3, 3, 3], [1, 4, 1], [1, 4, 2], [2, 4, 1], [1, 4, 3], [2, 4, 2],
# [3, 4, 1], [2, 4, 3], [3, 4, 2], [3, 4, 3]]