fregimus: (q)
[personal profile] fregimus
У меня возникла комбинаторная задачка со стековой машиной, которую было бы интересно проанализировать.

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

О Р ↑ ↑ К ↑

Берем карточки слева направо по одной. Карточка О кладется в стопку, Р поверх нее. Затем первая стрелка велит выложить верхнюю карточку Р на выход, а вторая выкладывает (теперь верхнюю) О. Затем К кладется в стопку и следом, по команде последней стрелки, выкладывается на выход за первыми двумя. В результате получаем на выходе

Р О К

Вопрос задачи состоит в следующем. Пусть нам даны исходная строка (в нашем примере это ОРК) и предположительная выходная (РОК). Можно ли так разложить карточки-команды со стрелкой между буквами исходной строки, чтобы на выходе получилась заданная? Буквы в строке не обязательно все различные, например, А Н А Н А С — допустимая строка.

Можете легко проверить, что из строки А Б В можно получить только 5 из 6 возможных перестановок, а перестановку В А Б получить невозможно.

А ↑ Б ↑ В ↑ → А Б В
А ↑ Б В ↑ ↑ → А В Б
А Б ↑ ↑ В ↑ → Б А В
А Б ↑ В ↑ ↑ → Б В А
А Б В ↑ ↑ ↑ → В Б А

Предпочтительно конструктивное решение (т. е. правило расстановки), и, конечно, самое простое. Существует очевидное решение «в лоб»: будем расставлять n стрелок, где n длина строки, в любом возможном порядке, и проверять, получится ли на выходе искомая строка. Но нам придется перебрать n! возможных комбинаций — громадное число даже для 10 букв, так что такое решение не годится. Есть очень простое линейное в n решение для строки с различающимися буквами, но оно не работает, если разрешить повторы одинаковых букв в строке. А дальше ваш ход.

Доб. Наврал я с линейным решением — нашел контрпример.
Tags:

(no subject)

2011-10-28 09:07 (UTC)
Posted by [identity profile] Исенбаев Владислав (from livejournal.com)
Есть решение за O(n^3), можно методом динамического программирования посчитать для каждой пары подстрок исходной и выходной строк посчитать, можно ли из первой получить вторую.
По этим данным легко восстановить и конструктцию.

(no subject)

2011-10-28 16:27 (UTC)
Posted by [identity profile] fregimus.livejournal.com
Попробую, спасибо.

(no subject)

2011-10-28 19:20 (UTC)
Posted by [identity profile] slobin.livejournal.com
Если я ничего не путаю, backtracking с меморизацией даёт результат строго не хуже, чем динамическое программирование. В процессе экспериментов выяснил, что "АБРАКАДАБРА" преобразуется в "РАБА БАРДАКА" тремя способами (пробел добавлен для красоты :-).

... Free As In Love ...

(no subject)

2011-10-28 21:25 (UTC)
Posted by [identity profile] fregimus.livejournal.com
Похоже. Что-то у меня от этого примера уже кора потрескивает. Я-то думал он простенький, дай, думаю, в ЖЖ напишу, домохозяйкам досуг скрашу. А оказалось, задачка-то вон какая!

(no subject)

2011-10-29 00:38 (UTC)
Posted by [identity profile] slobin.livejournal.com
Дальнейшее -- не ответ, а попытки думать вслух. Думаю вслух: а что не так с линейным решением? Вроде бы прав [livejournal.com profile] fat_crocodile: перебор возникает, только если есть повторяющиеся буквы. А если их нет, то переборный алгоритм вырождается в линейный. У меня, по крайней мере, вырождается. Можно мне тоже контрпример показать, вдруг у нас разные алгоритмы?

Ну, точнее, линейный он, если перестановку задать традиционным способом как перестановку 1..n: если там, как в ваших примерах, буквы (которые можно сравнивать только на равенство), то он превращается в квадратичный просто потому, что единственный способ узнать, что буква А ушла на пятое место -- это досчитать до пяти.

... Где они лежат, где они висят, где их точка? ...

(no subject)

2011-10-29 02:23 (UTC)
Posted by [identity profile] fregimus.livejournal.com
Хвосты строк равной длины должны быть одной и той же подстрокой, прочитываемой в разные стороны (длина хвоста однозначно определятся позицией последней буквы исходной строки в проверяемой), а головы, если оборвать эти хвосты, должны обладать тем же свойством. N шагов по одной строке и 2N по другой. Только с вашей абракадаброй не работает, даже если его дополнить бэктрекингом.
Edited 2011-10-29 02:24 (UTC)

(no subject)

2011-10-29 02:59 (UTC)
Posted by [identity profile] slobin.livejournal.com
Пытаюсь понять, похож ли ваш алгоритм на мой, если его применять слева направо: у меня всё крутится вокруг ПЕРВОЙ буквы исходной строки. Идея такая: если первая буква попала на k-тое место в выходной строке, значит, к этому моменту из стека ушло ровно k букв, и он в этот момент стал пустым. Значит, попало в него тоже ровно k букв (и все они ушли). Значит, левее k находится "правильная" перестановка 2..k первых букв, а правее -- k+1..n последних. Правильность для наглядности проверяем рекурсивно, но там суммарное количество вызовов получается n, а накладные затраты на рекурсию можно, кажется, убрать изощрённым программированием (но я поленился). А если есть несколько разных кандидатов на позицию, куда попала первая буква, то вот тут-то перебор и взлетает.

Реализация этой идеи в виде кода здесь (питон и генераторы (генераторы ведь нужны, чтобы варианты генерировать, правильно? :-)). Собственно решение задачи -- функция (генератор) transform, а evaluate -- это просто такой юнит тест, она собственно применяет все эти стрелочки и выдаёт итоговую перестановку (всегда одну и ту же, искомую). Важно: то, что генерятся правильные перестановки, я проверяю тупо и в лоб, там ошибиться вроде негде, а вот то, что генерятся ВСЕ правильные, что этот алгоритм ничего не пропускает -- не проверяю вообще. Если я лажанулся, то здесь. Выдача на тестовом примере:

abr!a!!!cadab!!r!!!!a! (True, 'rababardaca')
abr!a!!ca!dab!!r!!a!!! (True, 'rababardaca')
abr!a!!cada!b!ra!!!!!! (True, 'rababardaca')

... Глупое, злое и одноразовое ...

(no subject)

2011-10-29 04:22 (UTC)
Posted by [identity profile] fregimus.livejournal.com
Спасибо, я посмотрю завтра или в воскресенье. Сегодня много дел.

(no subject)

2011-10-29 02:58 (UTC)
Posted by [identity profile] fregimus.livejournal.com
Я что-то ни одного решения не могу найти вручную.

(no subject)

2011-10-29 10:08 (UTC)
Posted by [identity profile] Исенбаев Владислав (from livejournal.com)
Нет, backtracking'у мемоизация не поможет. Если ему скормить строки "ababab..ab" и "ababab..ac", он обойдет все возможные состояния стека, коих не меньше чем 2^(n/2)

(no subject)

2011-10-29 14:03 (UTC)
Posted by [identity profile] fat-crocodile.livejournal.com
ну, сначала можно вставить проверку на принципиальную получаемость одной строки из другой любыми перестановками.

(no subject)

2011-10-29 18:56 (UTC)
Posted by [identity profile] Исенбаев Владислав (from livejournal.com)
Даже с проверкой полиномиального времени не будет

(no subject)

2011-10-30 07:29 (UTC)
Posted by [identity profile] fat-crocodile.livejournal.com
не будет. Но привести пример уже не так просто.

Profile

fregimus: (Default)
fregimus

March 2014

S M T W T F S
       1
2 3456 78
910 1112 131415
16171819202122
23242526272829
3031     

Most Popular Tags

Page generated 2025-12-24 11:16

Expand Cut Tags

No cut tags