Я хочу преобразовать массив [3, 4, 8]
→ [3, 4, 8, 34, 38, 48, 43, 83, 84, 348, 384, 834, 843, 438, 483]
Я пробовал array.permutation
, но это только возвращается [348, 384, 834, 843, 438, 483]
. Есть ли чистый способ сделать это без предварительного создания всех возможных подмножеств?
Array#permutation принимает один необязательный параметр, указывающий, какой максимальный размер перестановки нужно создать.
В вашем случае вам просто нужны все перестановки размера 1
, затем 2
, затем 3
,... до вашего input.count
.
Итак, это так же просто, как создать диапазон этих чисел, сопоставить их, каждый раз вызывая permutation
с другим номером и комбинируя результаты:
def all_permutations_of(input)
(1..input.count).flat_map do |permutation_length|
input.permutation(permutation_length).to_a
end
end
p all_permutations_of([3, 4, 8])
# => [
# [3], [4], [8],
# [3, 4], [3, 8], [4, 3], [4, 8], [8, 3], [8, 4],
# [3, 4, 8], [3, 8, 4], [4, 3, 8], [4, 8, 3], [8, 3, 4], [8, 4, 3],
# ]
И если вы хотите, чтобы эти цифры были реальными числами, как показано в вашем примере, вам понадобится небольшая функция для их объединения, например, эта, которую я ласково называю «сглаживание»:
# Produces a single number from an array of digits
# E.g. `[1, 2, 3]` becomes `123`
def smoosh(digits)
exponent = digits.count
digits.sum do |digit|
exponent -= 1
digit * (10 ** exponent)
end
end
p all_permutations_of([3, 4, 8]).map { smoosh(_1) }
# => [3, 4, 8, 34, 38, 43, 48, 83, 84, 348, 384, 438, 483, 834, 843]
Вы можете объединить перестановки каждого размера вместе, а затем превратить их в массив:
ary = [3, 4, 8]
Enumerator::Chain.new(
*ary.map.with_index(1) { ary.permutation(_2) }
).map(&:join).map(&:to_i)
#=> [3, 4, 8, 34, 38, 43, 48, 83, 84, 348, 384, 438, 483, 834, 843]
map(&:join).map(&:to_i)
можно, конечно, комбинировать: map { |a| a.join.to_i }
. Лично у меня нет предпочтений.
В качестве альтернативы вы можете использовать Enumerator#+, чтобы объединить их и записать (1..ary.size).map { |i| ary.permutation(i) }.inject(:+).map { |a| a.join.to_i }
inject(:+)
on arrays создает множество промежуточных массивов. Ваше решение будет работать быстрее и будет более понятным, если вы используете Flatten или Flat_map, которые идеально подходят для этого случая.
Судя по вашему выбору ответа ниже, я предполагаю, что порядок элементов в желаемом массиве не важен. Если бы вы сказали это, некоторые из нас не стали бы терять время, принимая ваш вопрос за чистую монету.