Ruby — все перестановки с любым количеством элементов

Я хочу преобразовать массив [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]. Есть ли чистый способ сделать это без предварительного создания всех возможных подмножеств?

🤔 А знаете ли вы, что...
В Ruby все является объектом, включая числа и функции.


1
53
2

Ответы:

Решено

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]