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

Судя по вашему выбору ответа ниже, я предполагаю, что порядок элементов в желаемом массиве не важен. Если бы вы сказали это, некоторые из нас не стали бы терять время, принимая ваш вопрос за чистую монету.

Cary Swoveland 16.08.2024 02:13
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
1
1
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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]
map(&:join).map(&:to_i) можно, конечно, комбинировать: map { |a| a.join.to_i }. Лично у меня нет предпочтений.
Cary Swoveland 16.08.2024 02:34

В качестве альтернативы вы можете использовать Enumerator#+, чтобы объединить их и записать (1..ary.size).map { |i| ary.permutation(i) }.inject(:+).map { |a| a.join.to_i }

Stefan 16.08.2024 10:09
inject(:+) on arrays создает множество промежуточных массивов. Ваше решение будет работать быстрее и будет более понятным, если вы используете Flatten или Flat_map, которые идеально подходят для этого случая.
Alexander 16.08.2024 14:50

Другие вопросы по теме