Пасьянс со стеком
2011-10-28 00:33У меня возникла комбинаторная задачка со стековой машиной, которую было бы интересно проанализировать.
Представьте себе игру с перекладыванием карточек, целью которой является переставить карточки с буквами из исходного порядка в заданный. На каждой карточке написана или буква алфавита, или стрелка ↑. Правила перестановки букв таковы. Первоначально карточки с буквами и стрелкой выложены в ряд и берутся по одной слева направо. Если на карточке буква, то карточка кладется в стопку (если стопка пуста, то карточка будет в ней единственной, а если там уже есть карточки, то очередная карточка кладется поверх предыдущих). Если же на карточке стрелка, то эта карточка выбрасывается, а верхняя карточка из стопки выкладывается в отдельную строку для результата. Рассмотрим пример. Пусть задана последовательность карточек
О Р ↑ ↑ К ↑
Берем карточки слева направо по одной. Карточка О кладется в стопку, Р поверх нее. Затем первая стрелка велит выложить верхнюю карточку Р на выход, а вторая выкладывает (теперь верхнюю) О. Затем К кладется в стопку и следом, по команде последней стрелки, выкладывается на выход за первыми двумя. В результате получаем на выходе
Р О К
Вопрос задачи состоит в следующем. Пусть нам даны исходная строка (в нашем примере это ОРК) и предположительная выходная (РОК). Можно ли так разложить карточки-команды со стрелкой между буквами исходной строки, чтобы на выходе получилась заданная? Буквы в строке не обязательно все различные, например, А Н А Н А С — допустимая строка.
Можете легко проверить, что из строки А Б В можно получить только 5 из 6 возможных перестановок, а перестановку В А Б получить невозможно.
А ↑ Б ↑ В ↑ → А Б В
А ↑ Б В ↑ ↑ → А В Б
А Б ↑ ↑ В ↑ → Б А В
А Б ↑ В ↑ ↑ → Б В А
А Б В ↑ ↑ ↑ → В Б А
Предпочтительно конструктивное решение (т. е. правило расстановки), и, конечно, самое простое. Существует очевидное решение «в лоб»: будем расставлять n стрелок, где n длина строки, в любом возможном порядке, и проверять, получится ли на выходе искомая строка. Но нам придется перебрать n! возможных комбинаций — громадное число даже для 10 букв, так что такое решение не годится.Есть очень простое линейное в n решение для строки с различающимися буквами, но оно не работает, если разрешить повторы одинаковых букв в строке. А дальше ваш ход.
Доб. Наврал я с линейным решением — нашел контрпример.
Представьте себе игру с перекладыванием карточек, целью которой является переставить карточки с буквами из исходного порядка в заданный. На каждой карточке написана или буква алфавита, или стрелка ↑. Правила перестановки букв таковы. Первоначально карточки с буквами и стрелкой выложены в ряд и берутся по одной слева направо. Если на карточке буква, то карточка кладется в стопку (если стопка пуста, то карточка будет в ней единственной, а если там уже есть карточки, то очередная карточка кладется поверх предыдущих). Если же на карточке стрелка, то эта карточка выбрасывается, а верхняя карточка из стопки выкладывается в отдельную строку для результата. Рассмотрим пример. Пусть задана последовательность карточек
О Р ↑ ↑ К ↑
Берем карточки слева направо по одной. Карточка О кладется в стопку, Р поверх нее. Затем первая стрелка велит выложить верхнюю карточку Р на выход, а вторая выкладывает (теперь верхнюю) О. Затем К кладется в стопку и следом, по команде последней стрелки, выкладывается на выход за первыми двумя. В результате получаем на выходе
Р О К
Вопрос задачи состоит в следующем. Пусть нам даны исходная строка (в нашем примере это ОРК) и предположительная выходная (РОК). Можно ли так разложить карточки-команды со стрелкой между буквами исходной строки, чтобы на выходе получилась заданная? Буквы в строке не обязательно все различные, например, А Н А Н А С — допустимая строка.
Можете легко проверить, что из строки А Б В можно получить только 5 из 6 возможных перестановок, а перестановку В А Б получить невозможно.
А ↑ Б ↑ В ↑ → А Б В
А ↑ Б В ↑ ↑ → А В Б
А Б ↑ ↑ В ↑ → Б А В
А Б ↑ В ↑ ↑ → Б В А
А Б В ↑ ↑ ↑ → В Б А
Предпочтительно конструктивное решение (т. е. правило расстановки), и, конечно, самое простое. Существует очевидное решение «в лоб»: будем расставлять n стрелок, где n длина строки, в любом возможном порядке, и проверять, получится ли на выходе искомая строка. Но нам придется перебрать n! возможных комбинаций — громадное число даже для 10 букв, так что такое решение не годится.
Доб. Наврал я с линейным решением — нашел контрпример.
(no subject)
2011-10-28 08:22 (UTC)То есть нужно не просто найти подходящую букву и поставить после неё стрелочку, а попробовать варианты со всеми подходящими буквами.
(no subject)
2011-10-28 08:26 (UTC)(no subject)
2011-10-28 08:31 (UTC)(no subject)
2011-10-28 08:47 (UTC)(no subject)
2011-10-28 08:49 (UTC)(no subject)
2011-10-28 08:58 (UTC)(no subject)
2011-10-28 09:07 (UTC)По этим данным легко восстановить и конструктцию.
(no subject)
2011-10-28 10:06 (UTC)другое дело, что можно двигаться от конца к началу, или вообще неким образом рассматривать строку как целое, и ставить сначала те стрелочки, которые не могут быть не поставлены, и потом это упростит отбор вариантов.
(no subject)
2011-10-28 10:37 (UTC)(no subject)
2011-10-28 10:44 (UTC)я не имел ввиду, что Вы поставили такое ограничение, я имел ввиду, что
- не думал всерьёз о других способах решения -- просто потому что всерьёз над задачей не думал
- но среди алгоритмов, двигающихся по строке от начала к концу этот выглядит оптимальным. От конца к началу скорее всего тоже, наверняка там всё симметрично.
То есть остаются алгоритмы, не двигающиеся по строке, а как-то совсем иначе. Например, как написали ниже, что-нибудь с динамическим программированием и парами символов.
(no subject)
2011-10-28 16:27 (UTC)(no subject)
2011-10-28 16:27 (UTC)(no subject)
2011-10-28 16:28 (UTC)(no subject)
2011-10-28 19:20 (UTC)... Free As In Love ...
(no subject)
2011-10-28 21:25 (UTC)(no subject)
2011-10-28 22:39 (UTC)Покажите? Я просто пока не вижу разницы между любыми различными примерами, если буквы не повторяются, алгоритм совсем в лоб работает.
Или в общем виде? Но в общем Вы вроде на линейность и не претендовали.
(no subject)
2011-10-29 00:38 (UTC)Ну, точнее, линейный он, если перестановку задать традиционным способом как перестановку 1..n: если там, как в ваших примерах, буквы (которые можно сравнивать только на равенство), то он превращается в квадратичный просто потому, что единственный способ узнать, что буква А ушла на пятое место -- это досчитать до пяти.
... Где они лежат, где они висят, где их точка? ...
(no subject)
2011-10-29 02:23 (UTC)(no subject)
2011-10-29 02:58 (UTC)(no subject)
2011-10-29 02:59 (UTC)Реализация этой идеи в виде кода здесь (питон и генераторы (генераторы ведь нужны, чтобы варианты генерировать, правильно? :-)). Собственно решение задачи -- функция (генератор) transform, а evaluate -- это просто такой юнит тест, она собственно применяет все эти стрелочки и выдаёт итоговую перестановку (всегда одну и ту же, искомую). Важно: то, что генерятся правильные перестановки, я проверяю тупо и в лоб, там ошибиться вроде негде, а вот то, что генерятся ВСЕ правильные, что этот алгоритм ничего не пропускает -- не проверяю вообще. Если я лажанулся, то здесь. Выдача на тестовом примере:
... Глупое, злое и одноразовое ...
(no subject)
2011-10-29 04:22 (UTC)(no subject)
2011-10-29 10:08 (UTC)No title
2011-10-29 11:40 (UTC)(no subject)
2011-10-29 14:03 (UTC)(no subject)
2011-10-29 15:21 (UTC)начало:
- пустой стек
- индекс в строке входа на начале
- индекс в строке выхода на начале
шаг алгоритма:
- проверить текущий элемент строки выхода
- если он совпадает с верхушкой стека
---- поставить в строке входа стрелку в текущей позиции
---- выкинуть верхний элемент из стека
- иначе перемешаемся по строке входа вперёд и добавляем в стек элементы, пока не найдём искомый, ставим после него стрелочку
Если дошли до конца строки, а в стеке что-то не то -- значит не судьба.
Я не вижу, где тут может вкрасться нелинейность. Как объяснить это не ссылаясь на моё хорошее зрение:
- на каждом шаге мы либо снимаем верхний элемент стека -- O(1)
- либо движемся вперёд O(n)
- но суммарных движений вперёд не может быть больше чем n
значит в сумме это что-то типа O(2n) -- линейное.
(no subject)
2011-10-29 18:56 (UTC)(no subject)
2011-10-30 07:29 (UTC)(no subject)
2011-12-02 23:33 (UTC)Если все буковки различны, то есть два линейных алгоритма вычисления порядка пуш/попов, с начала и с конца. Оба со стеком, оба попают очередную буковку если она совпадает с вершиной стека. Прозреваю, что многих комментаторов смутили ваши стрелочки. Как можно оптимизировать их для ситуации с одинаковыми буковками -- не знаю. Особенно не знаю что делать если вообще все буковки одинаковы и решения нет.