генерировать последовательность со всеми перестановками - PullRequest
9 голосов
/ 12 февраля 2010

Как я могу сгенерировать самую короткую последовательность, содержащую все возможные перестановки?

Пример: Для длины 2 ответ - 121, потому что этот список содержит 12 и 21, которые являются всеми возможными перестановками.

Для длины 3 ответ 123121321, потому что этот список содержит все возможные перестановки: 123, 231, 312, 121 (недействительно), 213, 132, 321.

Каждое число (в пределах данной перестановки) может встречаться только один раз.

Ответы [ 4 ]

6 голосов
/ 13 февраля 2010

Этот жадный алгоритм создает довольно короткие минимальные последовательности.

ОБНОВЛЕНИЕ: обратите внимание, что для n & ge; 6, этот алгоритм не создает самую короткую строку!

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

Любопытный шаг разрыва связи необходим для правильности; вместо этого разрыв связи в случайном порядке приводит к более длинным строкам.

Я убедился (написав гораздо более длинную и медленную программу), что ответ, который этот алгоритм дает для длины 4, 123412314231243121342132413214321, действительно самый короткий ответ. Однако для длины 6 он дает ответ длиной 873, который длиннее, чем самое короткое из известных решений.

Алгоритм O ( n ! 2 ).

Реализация на Python:

import itertools

def costToAdd(a, b):
    for i in range(1, len(b)):
        if a.endswith(b[:-i]):
            return i
    return len(b)

def stringContainingAllPermutationsOf(s):
    perms = set(''.join(tpl) for tpl in itertools.permutations(s))
    perms.remove(s)
    a = s
    while perms:
        cost, next = min((costToAdd(a, x), x) for x in perms)
        perms.remove(next)
        a += next[-cost:]
    return a

Длина строк, генерируемых этой функцией, составляет 1, 3, 9, 33, 153, 873, 5913, ... что выглядит как эта целочисленная последовательность .

У меня есть предчувствие, что вы можете справиться лучше, чем O ( n ! 2 ).

5 голосов
/ 12 февраля 2010
  • Создать все перестановки.
  • Пусть каждый Перестановка представляет собой узел в граф.
  • Теперь для любых двух состояний добавьте край со значением 1, если они разделяют n-1 цифр (для источника из конец, и для цели из конец), два, если они разделяют n-2 цифр и так далее.
  • Теперь вам осталось найти кратчайший путь, содержащий n вершины.
3 голосов
/ 16 февраля 2010

Вот быстрый алгоритм, который выдает короткую строку, содержащую все перестановки. Я почти уверен, что он даст кратчайший ответ, но у меня нет полного доказательства в руках.

Объяснение. Ниже приведено дерево всех перестановок. Картина неполная; представьте, что дерево вечно направо.

1 --+-- 12 --+-- 123 ...
    |        |
    |        +-- 231 ...
    |        |
    |        +-- 312 ...
    |
    +-- 21 --+-- 213 ...
             |
             +-- 132 ...
             |
             +-- 321 ...

Узлы на уровне k этого дерева - все перестановки длины к . Кроме того, перестановки в определенном порядке с большим перекрытия между каждой перестановкой и ее соседями сверху и снизу.

Если быть точным, первый дочерний узел каждого узла можно найти, просто добавив следующий символ до конца. Например, первый ребенок 213 будет 2134. Остальные детей можно найти, повернув к первому ребенку, чтобы оставить один символ в время. Вращение 2134 даст 1342, 3421, 4213.

Взятие всех узлов на заданном уровне и связывание их вместе, перекрывая максимально, выдает строки 1, 121, 123121321 и т. д.

Длина n -ой строки в этой последовательности равна сумма для x = от 1 до n из x! . (Вы можете доказать это, наблюдая, сколько не перекрывается между соседними перестановками. Братья и сестры перекрываются во всех символах, кроме 1; двоюродные братья перекрываются во всех символах, кроме 2; и т. Д.)

Набросок доказательства. Я не полностью доказал, что это лучшее решение, но вот набросок того, как будет проходить доказательство. Сначала покажите, что любая строка, содержащая n различных перестановок, имеет длину & ​​ge; 2 n - 1. Затем покажите, что добавление любой строки, содержащей n + 1 различных перестановок, имеет длину 2 n + 1. То есть добавление еще одной перестановки приведет к стоить вам две цифры. Приступить к расчету минимальной длины строк, содержащих n P r и n P r + 1 отдельная перестановка, до n! . Короче говоря, эта последовательность оптимальна, потому что вы не можете сделать ее хуже где-то в надежде улучшить ее где-то еще. Это уже локально оптимально везде. Все ходы являются принудительными.

Алгоритм. Учитывая весь этот фон, алгоритм очень прост. Пройдите по этому дереву на нужную глубину и объедините все узлы на этой глубине.

К счастью, нам не нужно строить дерево в памяти.

def build(node, s):
    """String together all descendants of the given node at the target depth."""
    d = len(node)  # depth of this node. depth of "213" is 3.
    n = len(s)     # target depth
    if d == n - 1:
        return node + s[n - 1] + node    # children of 213 join to make "2134213"
    else:
        c0 = node + s[d]                 # first child node
        children = [c0[i:] + c0[:i] for i in range(d + 1)]  # all child nodes
        strings = [build(c, s) for c in children]  # recurse to the desired depth
        for j in range(1, d + 1):
            strings[j] = strings[j][d:]  # cut off overlap with previous sibling
        return ''.join(strings)          # join what's left

def stringContainingAllPermutationsOf(s):
    return build(s[:1], s)

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

0 голосов
/ 04 декабря 2018

Для n 3 длина цепи составляет 8 12312132 Сдается мне, мы работаем с циклической системой - это звон, говоря другими словами. Но мы работаем с кольцом, как будто оно цепное. Цепочка действительно 123121321 = 9 Но кольцо 12312132 = 8 Мы берем последнюю 1 за 321 от начала последовательности 1 2312132 [1].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...