Как получить доступ к списку перестановок в прологе? - PullRequest
3 голосов
/ 03 февраля 2012

Я хочу получить доступ к перестановке списка и передать ее в качестве аргумента другим функциям.

Это код перестановки:

takeout(X,[X|R],R).  
takeout(X,[F|R],[F|S]) :-
   takeout(X,R,S),
   write(S).

perm([X|Y],Z) :-
   perm(Y,W),
   takeout(X,Z,W).  
perm([],[]).

Ответы [ 3 ]

9 голосов
/ 04 февраля 2012

Для начала давайте переопределим ваши предикаты, чтобы они не делали ненужных операций ввода-вывода:

takeout(X,[X|R],R).  
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).

perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).  
perm([],[]).

Теперь у вас есть то, что можно считать «чистой» функцией перестановки:

?- perm([1,2,3], X).
X = [1, 2, 3] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [1, 3, 2] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.

Итак, предположим, что у вас есть функция max_heap, которая принимает список значений и создает дерево. Я позволю вам позаботиться об этом, поэтому давайте просто постулируем, что он существует и называется max_heap/2, и давайте далее постулируем, что у вас есть способ отобразить это привлекательно называемое display_heap/1. Чтобы «взять» перестановку и «отправить» ее в качестве параметра этим функциям, вы действительно говорите в математике: предположим, что P - это перестановка X, давайте создадим с ним max_heap и отобразим его. Или предположим, что P - это перестановка X, H - максимальная куча из X, давайте покажем H:

show_heaps(List) :- perm(List, P), max_heap(P, H), display_heap(H).

Это говорит о том же, что и мое английское предложение: предположим, что P - перестановка списка, затем H - его представление в виде кучи, затем отобразим его. Технически, display_heap/1 все еще является предикатом, который может быть истинным или ложным для данной кучи. На практике это всегда будет верно, и если вы запустите это, вам все равно придется многократно нажимать ;, чтобы сказать: «Дайте мне другое решение, если только вы не используете цикл, управляемый ошибками, или внелогичный предикат, такой как findall/3» вызвать все решения, которые будут найдены.

Редактировать : Давайте обсудим циклы с ошибками и findall/3. Сначала позвольте мне добавить несколько новых предикатов, потому что я точно не знаю, что вы делаете, но это не имеет значения для наших целей.

double([X|Xs], [Y|Ys]) :- Y is X*2, double(Xs, Ys).
double([],[]).

showlist(Xs) :- print(Xs).

Итак, теперь у меня есть предикат double/2, который удваивает значения в списке, и предикат showlist/1, который выводит список на стандартный вывод. Мы можем попробовать это так:

?- perm([1,2,3], X), double(X, Y), showlist(Y).
[2,4,6]
X = [1, 2, 3],
Y = [2, 4, 6] ;
[4,2,6]
X = [2, 1, 3],
Y = [4, 2, 6] ;
[4,6,2]
X = [2, 3, 1],
Y = [4, 6, 2] ;
[2,6,4]
X = [1, 3, 2],
Y = [2, 6, 4] ;
[6,2,4]
X = [3, 1, 2],
Y = [6, 2, 4] ;
[6,4,2]
X = [3, 2, 1],
Y = [6, 4, 2] ;
false.

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

?- perm([1,2,3], X), double(X, Y), showlist(Y), fail.
[2,4,6][4,2,6][4,6,2][2,6,4][6,2,4][6,4,2]
false.

Итак, теперь вы видите, что выходные данные каждой перестановки прошли там double/2, а затем Пролог сообщил о ложном. Вот что под этим подразумевается:

show_all_heaps(List) :- perm(List, X), double(X, Y), showlist(Y), nl, fail.
show_all_heaps(_).

Посмотрите, как это работает:

?- show_all_heaps([1,2,3]).
[2,4,6]
[4,2,6]
[4,6,2]
[2,6,4]
[6,2,4]
[6,4,2]
true.

Другой вариант использует findall/3, который выглядит примерно так:

?- findall(Y, (perm([1,2,3], X), double(X, Y)), Ys).
Ys = [[2, 4, 6], [4, 2, 6], [4, 6, 2], [2, 6, 4], [6, 2, 4], [6, 4, 2]].

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

2 голосов
/ 19 октября 2015

Мы можем определить list_permutation/2 на основе same_length/2 и select/3 следующим образом:

:- use_module(library(lists),[same_length/2,select/3]).

list_permutation(As,Bs) :-
   same_length(As,Bs),          % redundant goal helps termination
   list_permutation_(As,Bs).

list_permutation_([],[]).
list_permutation_([A|As],Bs0) :-
    select(A,Bs0,Bs),
    list_permutation_(As,Bs).

Благодаря same_length/2 обаследующие запросы 1,2 завершаются повсеместно:

?- list_permutation([1,2,3],Ys).
  Ys = [1,2,3]
; Ys = [1,3,2]
; Ys = [2,1,3]
; Ys = [3,1,2]
; Ys = [2,3,1]
; Ys = [3,2,1]
; false.

?- list_permutation(Xs,[1,2,3]).
  Xs = [1,2,3]
; Xs = [1,3,2]
; Xs = [2,1,3]
; Xs = [2,3,1]
; Xs = [3,1,2]
; Xs = [3,2,1]
; false.

Пока все хорошо.Но как выглядит последовательность ответов при наличии повторяющихся элементов списка?

?- list_permutation([1,1,1],Ys).
  Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; false.

5/6 излишни!Что мы можем сделать?Мы просто используем select<b>d</b>/3 вместо select/3!

list_permuted(As,Bs) :-
   same_length(As,Bs),
   list_permuted_(As,Bs).

list_permuted_([],[]).
list_permuted_([A|As],Bs0) :-
   selectd(A,Bs0,Bs),           % use selectd/3, not select/3
   list_permuted_(As,Bs).

Давайте повторно запустим запрос, который дал нам 5 избыточныхрешения раньше!

?- list_permuted([1,1,1],Ys).
  Ys = [1,1,1]
; false.

?- list_permuted(Xs,[1,1,1]).
  Xs = [1,1,1]
; false.

Лучше! Все лишние ответы исчезли.

Давайте сравним набор решений для некоторого примера:

?- _Xs = [1,2,1,1,2,1,1,2,1],
   setof(Ys,list_permutation(_Xs,Ys),Yss),
   setof(Ys,list_permuted(_Xs,Ys),Yss),
   length(Yss,N).
N = 84, Yss = [[1,1,1,1,1,1,2,2,2],[1,1,1,1,1,2,1,2,2],[...|...]|...].

ОК!Как насчет эмпирических измерений во время выполнения с проблемой немного большего размера?Мы используем call_time/2 для измерения времени выполнения в миллисекундах T_ms.

?- call_time(\+ (list_permutation([1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 8110.

?- call_time(\+ (list_permuted(   [1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 140.

ОК!А при правильной компиляции if_/3 и (=)/3, list_permuted/2 еще быстрее!


Сноска 1: Использование SICStus Prolog версии 4.3.2 (x86_64-linux-glibc2.12). Сноска 2: Ответы, данные на уровне Пролога, были постобработаны для удобства чтения.

1 голос
/ 10 июня 2018

Если вы просто хотите исследовать перестановки без «False» в конце, этот код может быть полезен

takeout(X,[F |R],[F|S]) :- F\=X, takeout(X,R,S).
takeout(X,[X|R],R).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).  
perm([],[]).

Итак, вывод perm ([a, b], B) будетбыть

B=[a,b]
B=[b,a]
...