Рубрики
Без рубрики

Эффективно вычислять перестановки

Перестановка: Каждый из нескольких возможных способов, которым набор или количество вещей можно заказать или ARR … помечен Python, алгоритмы.

Перестановка : Каждый из нескольких возможных способов, которым набор или количество вещей можно заказать или устроить.

Учитывая несколько предметов, может быть интересно найти все возможные способы организовать их. Например, строка ABC имеет шесть перестановок: Abc. , ACB , BAC , BCA , Кабина , CBA Отказ Обратите внимание, что количество разрешений является факториал количества предметов. Это происходит потому, что мы можем выбрать любой из предметов, как первый, у нас есть # retems - 1 Выбор на второй и так далее, пока мы не достигнем последнего элемента с одним выбором.

Мы могли бы выделить все перестановки рекурсивно, как показано в этой функции Python:

def permutations(items):
  if len(items) == 1:
    return [items]
  result = []
  # We keep only the distinct items to avoid returning duplicates
  for item in list(set(items)):
    others = items[:]
    others.remove(item)
    for permutation in permutations(others):
      # Note: if we want lexicographical order we should insert in front
      # assuming that items are sorted
      permutation.append(item)
      result.append(permutation)
  return result

Мы могли бы выполнить это и получить результаты, которые мы ожидали:

>>> permutations([1,2,3])
[[3, 2, 1], [2, 3, 1], [3, 1, 2], [1, 3, 2], [2, 1, 3], [1, 2, 3]]
>>> permutations([1,2,1])
[[2, 1, 1], [1, 2, 1], [1, 1, 2]]

Это решение работает, это приятно и просто, но он не очень эффективен: для нас требуется, чтобы мы создали много копий списка элементов, чтобы мы могли передать их на следующий рекурсивный шаг. Другим недостатком является то, что эта функция вычисляет (и сохраняет память) все Перестановки, которые это отходы, если нам нужна лишь немногие из них.

Мы могли бы посмотреть на это с другого угла и задавать себя, если есть способ, которым мы могли бы взять список предметов и узнать только Следующий Перестановка, что бы это ни значило.

Если говорить о Следующий Перестановка мы подразумеваем, что между ними есть порядок. В принципе определение перестановок не подразумевает никакого порядка, но мы всегда можем добавить себя на себя и требовать некоторых заказов. Используя лексикографический заказ это хороший выбор, который позволяет нам генерация Следующая перестановка для заданного списка предметов.

Возвращаясь к нашему первоначальному примеру, мы могли бы начать с ABC , реорганизуйте предметы, чтобы получить ACB Сделайте это еще раз, чтобы получить BAC , и так далее. Это позволит нам вычислить все перестановки, но на этот раз один из раз и без ненужных копий списка.

Первый список в лексикографическом порядке имеет все его предметы в не уменьшается Заказ: для всех I: предметы [я] [I + 1] Отказ В нашем примере у нас есть ABC и A и B . Наоборот, последний товар имеет все свои предметы в не увеличивается Заказ: для всех I: предметы [я] [I + 1] Отказ В нашем примере у нас есть CBA и C> B. и B> A Отказ

Наш алгоритм должен заставить нас перейти от этих двух крайностей, переставив элементы, так что каждая перестановка «увеличивает» последовательность по минимальному количеству. Ключевое наблюдение состоит в том, что мы хотим искать самый длинный суффикс в не уменьшается заказ (то есть это уже самый большой возможный). Давайте назовем это суффикс Отказ Для того, чтобы сделать некоторый прогресс, нам нужно рассмотреть все предметы в суффикс И тот, кто непосредственно перед этим, который мы называем вращаться . Если мы начнем с 0125430 Мы могли разделить строку в префикс , Пивор и суффикс Отказ

Мы ищем минимальные изменения, поэтому мы не должны касаться приставка . Если мы сделаем, мы получим большее изменение, чем мы хотим. Мы можем двигаться только Пивор и предметы в суффикс Отказ Мы также должны сделать это, чтобы удовлетворить эти два условия, чтобы иметь возможность продвинуться к следующей перестановке в лексикографическом порядке:

  • Нам нужно заменить Пивор с наименьшим возможным значением;
  • Мы должны перераспределить предметы, такие что новый суффикс минимален.

Мы можем удовлетворить первое условие, заменив Пивор С наименьшим предметом в суффикс больше чем Пивор сам. Принимая меньшую ценность, означало бы идти назад, приема большую ценность будет означать слишком далеко. В нашем примере мы получим приставка , Пивор и суффикс Отказ

Это шаг в правильном направлении, но мы все еще удовлетворяем второе условие. У нас есть максимальный суффикс, мы можем сделать это минимальным путем просто отменить его, что имеет эффект поворота не уменьшается заказ на не увеличивается порядок мы после. В нашем примере мы получим приставка , Пивор и суффикс Отказ Мы закончили, следующая перестановка – 0130245.

Алгоритм

Алгоритм для реализации этого состоит из следующих шагов:

  1. Найти самый большой к Такое, что [K] . Если мы не можем найти его, товар, на котором мы смотрим, уже последний в лексикографическом порядке. Мы могли бы найти следующий товар только Оберните вокруг и генерировать первую перестановку в лексикографическом порядке.
  2. Найти самый большой индекс л больше чем к Такое, что [K] Отказ
  3. Своп [K] и [l] Отказ
  4. Обратитесь в последовательность от [K + 1] до и включая последний элемент [n] Отказ

В коде это выглядит как:

def next_permutation(items):
  n = len(items)
  # Find pivot
  k = None
  for i in range(n-1):
    if(items[i] < items[i+1]):
      k = i
  if k is None:
    # items is maximal. We need to wrap around
    return None
  # Find new pivot
  for i in range(k, n):
    if(items[k] < items[i]):
      l = i
  # Swap pivot
  items[k], items[l] = items[l], items[k]
  # Reverse suffix
  for i in range(k+1, int((k+1+n)/2)):
    items[i], items[n-i] = items[n-i], items[i]
  return items

И мы можем использовать его следующим образом:

>>> permutation = [1,2,3]
>>> while permutation != None:
... print(permutation)
... permutation = next_permutation(permutation)
...
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

Это не единственный возможный подход к генерированию перестановок. На самом деле есть Несколько других алгоритмов Отказ Я нахожу Алгоритм кучи Целью интересным, потому что ему удается создать следующую перестановку, подняв одну пару элементов.

Благодаря Проект NAYUKI Для поста, который вдохновил меня, чтобы посмотреть в этот алгоритм.

Оригинал: “https://dev.to/mariosangiorgio/efficiently-compute-permutations-2phk”