[Prolog] Задачка на пермутации
От: Juster  
Дата: 09.04.07 20:41
Оценка:
Помогите решить задачку о нахождении всех перестановок элементов списка на прологе? Это не для вуза, для самообразования. Долго пытался, но не смог. Что-то важное не могу постичь.
DOMAINS
    list = integer*
PREDICATES
    permutations(list, list)

Например, для permutations([1,2,3], X) должен вывести (не обязательно в таком порядке):
X=[1,2,3]
X=[1,3,2]
X=[2,1,3]
X=[2,3,1]
X=[3,1,2]
X=[3,2,1]
Re: [Prolog] Задачка на пермутации
От: Кодт Россия  
Дата: 10.04.07 07:39
Оценка:
Здравствуйте, Juster, Вы писали:

J>Помогите решить задачку о нахождении всех перестановок элементов списка на прологе? Это не для вуза, для самообразования. Долго пытался, но не смог. Что-то важное не могу постичь.


Можно пойти по определению:
permutation(Xs,Ys) отвечает на вопрос: является ли Xs перестановкой Ys (и наоборот).
Обрати внимание на характер постановки задачи. Это не функция permutations : *int -> **int, возвращающая список списков (все перестановки), а предикат, "возвращающий" одну перестановку. Перебор осуществляется откатами.
Это фундаментальное отличие логического программирования от, например, функционального. (Хотя, разумеется, ФП можно выразить через ЛП и наоборот).

permutation(X,X). % идентичная перестановка
permutation([X|Xt],[X|Yt]) :- permutation(Xt,Yt). % если совпадают головы, то нужно проверить перестановку хвостов
permutation([X|Xt],[Y|Yt]) :-
    extract(Yt,X,Zt), % Zt = Yt \ [X]
    permutation(Xt,[Y|Zt]).

Тут видно, что последние два клоза (да, в общем-то, и первый) можно объединить — какая разница, извлекать со второй позиции, или с первой
permutation([X|Xt],Yt) :-
    extract(Yt,X,Zt),
    permutation(Xt,Zt).

Теперь остаётся реализовать более простой предикат extract
extract([X|Xt],X,Xt).
extract([Y|Yt],X,[Y|Zt]) :- extract(Yt,X,Zt).


Единственно, нужно убедиться, что предикаты имеют "обратную силу", т.е. способны не только проверять, но и порождать все возможные факты (без повторений!).

Пример: очевидно, что
equalLists(Xs,Xs).
equalLists([X|Xt],[X|Yt]) :- equalLists(Xt,Yt).

при запросе equalLists([1,2,3],Ys) даст 8 одинаковых ответов Ys=[1,2,3]
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: [Prolog] Задачка на пермутации
От: Juster  
Дата: 10.04.07 07:53
Оценка: 27 (1)
Ну ладно, скажу честно, было поздно и голова не хотела думать (как обычно)... а теперь я подумал хорошенько и нашел в гугле
PREDICATES
    position(list, integer, list)
    transposition(list, list)

CLAUSES  
    position(L, X, [X|L]).
    position([H|T1], X, [H|T2]) :- 
         position(T1,X,T2).

    transposition([],[]).
    transposition([H|T],L) :-
         transposition(T,T1),
         position(T1,H,L).
Re[2]: [Prolog] Задачка на пермутации
От: Juster  
Дата: 10.04.07 08:08
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Можно пойти по определению:

К>permutation(Xs,Ys) отвечает на вопрос: является ли Xs перестановкой Ys (и наоборот).
К>Обрати внимание на характер постановки задачи. Это не функция permutations : *int -> **int, возвращающая список списков (все перестановки), а предикат, "возвращающий" одну перестановку. Перебор осуществляется откатами.
К>Это фундаментальное отличие логического программирования от, например, функционального. (Хотя, разумеется, ФП можно выразить через ЛП и наоборот).

К>
К>permutation(X,X). % идентичная перестановка
К>permutation([X|Xt],[X|Yt]) :- permutation(Xt,Yt). % если совпадают головы, то нужно проверить перестановку хвостов
К>permutation([X|Xt],[Y|Yt]) :-
К>    extract(Yt,X,Zt), % Zt = Yt \ [X]
К>    permutation(Xt,[Y|Zt]).
К>

К>Тут видно, что последние два клоза (да, в общем-то, и первый) можно объединить — какая разница, извлекать со второй позиции, или с первой
К>
К>permutation([X|Xt],Yt) :-
К>    extract(Yt,X,Zt),
К>    permutation(Xt,Zt).
К>

К>Теперь остаётся реализовать более простой предикат extract
К>
К>extract([X|Xt],X,Xt).
К>extract([Y|Yt],X,[Y|Zt]) :- extract(Yt,X,Zt).
К>


К>Единственно, нужно убедиться, что предикаты имеют "обратную силу", т.е. способны не только проверять, но и порождать все возможные факты (без повторений!).


Большое спасибо, заставили думать.
Мне кажется, что задачи в прологе как и в императивных языках проще решать сверху, сначала описываем как будет решаться задача, а потом опускаемся по уровню вплоть до самых простых предикатов.
У меня проблема в решении задач состоит в преодолении какого-то барьера... бывает не могу решить задачу (а решаю я только обучающие задачи из головы и всяких книжек), а посмотрю решение и все понятно...

К>Пример: очевидно, что

К>
К>equalLists(Xs,Xs).
К>equalLists([X|Xt],[X|Yt]) :- equalLists(Xt,Yt).
К>

К>при запросе equalLists([1,2,3],Ys) даст 8 одинаковых ответов Ys=[1,2,3]
Не понял, что из этого следует?
Re[2]: [Prolog] Задачка на пермутации
От: Juster  
Дата: 10.04.07 08:49
Оценка:
К>
К>permutation(X,X). % идентичная перестановка
К>permutation([X|Xt],[X|Yt]) :- permutation(Xt,Yt). % если совпадают головы, то нужно проверить перестановку хвостов
К>permutation([X|Xt],[Y|Yt]) :-
К>    extract(Yt,X,Zt), % Zt = Yt \ [X]
К>    permutation(Xt,[Y|Zt]).
К>

К>Тут видно, что последние два клоза (да, в общем-то, и первый) можно объединить — какая разница, извлекать со второй позиции, или с первой
К>
К>permutation([X|Xt],Yt) :-
К>    extract(Yt,X,Zt),
К>    permutation(Xt,Zt).
К>

К>Теперь остаётся реализовать более простой предикат extract
К>
К>extract([X|Xt],X,Xt).
К>extract([Y|Yt],X,[Y|Zt]) :- extract(Yt,X,Zt).
К>


Проверил extract, он удаляет только один элемент из списка. Например:
GOAL extract([2,1,2,1], 1, X).
Выдает:
[2, 2, 1]
[2, 1, 2]

Хотя, кажется, для задачи этого достаточно.

Но самое главное, что предикат permutation не выполняется, т.к. при просмотре первого определения extract, Xt — не bound. Если продолжить, несмотря на замечание, то heap overflow.

Пошел думать над extract.
Re[3]: [Prolog] Задачка на пермутации
От: Кодт Россия  
Дата: 10.04.07 09:11
Оценка:
Здравствуйте, Juster, Вы писали:

К>>Пример: очевидно, что

К>>equalLists(Xs,Xs).
К>>equalLists([X|Xt],[X|Yt]) :- equalLists(Xt,Yt).

К>>при запросе equalLists([1,2,3],Ys) даст 8 одинаковых ответов Ys=[1,2,3]
J>Не понял, что из этого следует?

Из того, что оба клоза дают одинаковый ответ.
Если мы напишем запрос с циклом
?- equalLists([1,2,3],Ys), write(Ys), fail. % чтобы перебрать и вывести все варианты

то увидим.

Достаточно потрассировать, чтобы убедиться.
Только я ошибся: не 8, а 4. Потому что для пустого списка подходит только первый клоз, поэтому общее число вариантов — 2^(n-1) где n — длина списка.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: [Prolog] Задачка на пермутации
От: Кодт Россия  
Дата: 10.04.07 10:12
Оценка:
Здравствуйте, Juster, Вы писали:

J>Проверил extract, он удаляет только один элемент из списка. Например:

J>
J>GOAL extract([2,1,2,1], 1, X).
J>Выдает:
J>[2, 2, 1]
J>[2, 1, 2]
J>

J>Хотя, кажется, для задачи этого достаточно.

Не просто достаточно, но и необходимо!

J>Но самое главное, что предикат permutation не выполняется, т.к. при просмотре первого определения extract, Xt — не bound. Если продолжить, несмотря на замечание, то heap overflow.


Вот как раз об этом я и говорил. И, похоже, сам наступил на грабли Предикат умеет проверять, но не умеет выводить.

А ещё
permutation([], []). % забыл вот это. без него рекурсия не пройдёт.


Сейчас подумаю, как рекуррентный предикат определить.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: [Prolog] Задачка на пермутации
От: Трурль  
Дата: 10.04.07 11:59
Оценка:
permutation([],[]).
permutation(Xs,[Y|Ys]):- extract(Xs,Y,Zs), permutation(Zs,Ys).

или так
permutation([],[]). 
permutation([X|Xs],Ys):- permutation(Zs,Xs), extract(Zs,X,Ys).
Re: [Prolog] Задачка на пермутации
От: Кодт Россия  
Дата: 10.04.07 12:12
Оценка:
Немножко подумал и хочу заметить вот что.

Стоит различать перестановки уникальных и неуникальных элементов.
Разница в том, что в первом случае мы получаем ровно N! перестановок N-элементных наборов, а во втором — можем получить меньше.
И если у нас уникальность формальная, т.е. мы не занимаемся сравнениями элементов, то скормив такому алгоритму на вход набор с дублями, получим дубли наборов.
Алгоритм, который ты привёл — именно такой.

Важный момент — определение эквивалентности.
Если эквивалентность определена через порядок (x=y <=> ~(x<y)&~(y<x)), то можно воспользоваться алгоритмом Кнута next_permutation, порождающим лексикографически упорядоченную последовательность перестановок. Для чисел это очень естественно.
Если же эквивалентность дана сама по себе, то придётся повыкручиваться.

Возможное решение — отобразить список "чего попало" на список целых чисел и дальше использовать алгоритм Кнута.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.