Равенство множеств - PullRequest
5 голосов
/ 22 июня 2019

Может ли кто-нибудь помочь мне со следующей задачей: мне нужно определить предикат eq_set, который успешно выполняется, если наборы S1 и S2 равны, когда речь идет о количестве их элементов.

Но это работает только в том случае, если они в точности совпадают по номеру и порядку. Я хочу создать код, который показывает все разновидности и не учитывает порядок. Можете ли вы помочь мне, пожалуйста?

Я написал:

eq_set([],[]).
eq_set([H|T],[H|T1]) :-
    eq_set(T,T1).

Но это работает только в том случае, если они имеют одинаковый номер и порядок. Я хочу создать код, который показывает все разновидности и не учитывает порядок.

Ближайший перевод, который у меня есть, на болгарском языке: «Определить предикат eq_set, который успешно выполняется, если наборы (S1, S2) совпадают.

Ответы [ 4 ]

2 голосов
/ 23 июня 2019

На самом деле на самом деле зависит от того, как будет использоваться предикат.

Предполагая, что «множество» действительно является списком Пролога без дубликатов, но не в каком-либо конкретном порядке; тогда два набора в этом представлении «совпадают», если они являются перестановками друг друга. Другими словами, было бы достаточно определить eq_set/2 как:

eq_set(A, B) :-
    my_permutation(A, B).

и просто используйте определение учебника permutation/2, в котором используется определение учебника select/3 (см. «Искусство пролога (второе издание)» Стерлинга и Шапиро, стр. 67-9):

my_permutation([], []).
my_permutation(Xs, [Y|Ys]) :-
    my_select(Y, Xs, Xs0),
    my_permutation(Xs0, Ys).

my_select(X, [X|Xs], Xs).
my_select(X, [Y|Ys], [Y|Zs]) :-
    my_select(X, Ys, Zs).

(Я переименовал их только для того, чтобы убедиться, что я не использую определения стандартной библиотеки; в SWI-Prolog есть и select/3, и permutation/2 в автозагрузке библиотека (списки) ; определения в основном те же, но они выполняют некоторую проверку типов во время выполнения для аргументов.)

Вот как вы можете его использовать:

?- eq_set([1,2,3], [2,3,1]).
true ;
false.

?- eq_set([1,2,3], S).
S = [1, 2, 3] ;
S = [1, 3, 2] ;
S = [2, 1, 3] ;
S = [2, 3, 1] ;
S = [3, 1, 2] ;
S = [3, 2, 1] ;
false.

?- eq_set([1,2,3], [1,2]).
false.

?- eq_set(A, B).
A = B, B = [] ;
A = B, B = [_4480] ;
A = B, B = [_4480, _4492] ;
...

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

?- length(S1, _), eq_set(S1, S2), numbervars(S1).
S1 = S2, S2 = [] ;
S1 = S2, S2 = [A] ;
S1 = S2, S2 = [A, B] ;
S1 = [A, B],
S2 = [B, A] ;
S1 = S2, S2 = [A, B, C] ;
S1 = [A, B, C],
S2 = [A, C, B] ;
S1 = [A, B, C],
S2 = [B, A, C] ;
S1 = [A, B, C],
S2 = [B, C, A] ;
S1 = [A, B, C],
S2 = [C, A, B] ;
S1 = [A, B, C],
S2 = [C, B, A] ;
S1 = S2, S2 = [A, B, C, D] .

(Не беспокойтесь о numbervars, он просто дает читаемые имена всем свободным переменным в наборах. Имейте в виду, что объединение двух свободных переменных делает их одной и той же переменной.)

Это отправная точка, но, возможно, она уже достаточно хороша. Самым вопиющим упущением является то, что аргументы не должны быть списками без дубликатов. Один из способов определить это - потребовать, чтобы каждый элемент отличался от всех других элементов. Поскольку слово «отличается» является коммутативным, вы можете определить его следующим образом:

is_set([]).
is_set([X|Xs]) :-
    all_different(Xs, X),
    is_set(Xs).

all_different([], _).
all_different([Y|Ys], X) :-
    dif(X, Y),
    all_different(Ys, X).

Используется dif/2, который является широко доступным предикатом (но есть ли у вашего Пролога?).

Возможно, мы использовали бы maplist для последнего:

is_set([]).
is_set([X|Xs]) :-
    maplist(dif(X), Xs).
    is_set(Xs).
1 голос
/ 27 июня 2019

Вы достаточно близки в своем решении.

У нас есть два случая 1) Первый аргумент списка больше 2) Второй аргумент списка больше

Если вы уже знаете, какой из нихбольше, вы можете просто сделать

%in case the left one is longer
eq_set_left(_,[]).
eq_set_left(X,[H|T]):-member(H,X), eq_set_left(X,T).

Таким образом, очень простое и, тем не менее, оптимальное решение может быть следующим:

%in case the right one is longer
eq_set_right([],_).
eq_set_right([H|T], X):- member(H,X), eq_set_right(T,X).

%in case the left one is longer
eq_set_left(_,[]).
eq_set_left(X,[H|T]):-member(H,X), eq_set_left(X,T).

%both cases, equal length is also included here
eq_set(X,Y):- eq_set_left(X,Y).
eq_set(X,Y):- eq_set_right(X,Y).

eq_set (X, Y) - true, если любой X является подмножествомY, Y является подмножеством X или они равны

1 голос
/ 23 июня 2019

Вы называете их «множествами», но используемая вами структура данных - это список.Проще всего отсортировать два списка:

eq_set(A, B) :-
    % prerequisites: A and B are lists without duplicates
    sort(A, S),
    sort(B, S).

Если вы хотите что-то более сложное (по какой-то причине), вам нужно быть более конкретным.

С этим определением:

?- eq_set([a,b,c], [a,b]).
false. % OK

?- eq_set([a,b,c], [a,b,c]).
true. % OK

?- eq_set([a,c,b], [a,b,c]).
true. % OK

?- eq_set([a,a,b], [a,b,b]).
true. % Not sure....
0 голосов
/ 25 июня 2019

A set определяется как набор различных вещей, где порядок не важен, то есть наборы {a,b,c} и {b,a,c} идентичны.

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

Из этого можно просто сказать:

eq_set(Xs,Ys) :-
   findall( (Xs,Ys) , ( member(X,Xs), \+ member(X,Ys) ), [] ),
   findall( (Xs,Ys) , ( member(Y,Ys), \+ member(Y,Xs) ), [] )
   .

Или, если вы не хотите использовать встроенный findall/3,

eq_set(Xs,Ys) :-
  a_not_in_b( Xs , Ys , [] ) ,
  a_not_in_b( Ys , Xs , [] ) .

a_not_in_b( []     , [] , [] ) .
a_not_in_b( [A|As] , Bs , Xs ) :- member(A,Bs) , a_not_in_b( As, Bs,    Xs  ) .
a_not_in_b( [A|As] , Bs , Xs ) :-                a_not_in_b( As, Bs, [A|Xs] ) .

Нужнообратите внимание, что оба из них имеют производительность примерно O (N 2 ). Если рассматриваемые наборы велики, вы можете сначала отсортировать каждый набор, а затем объединить два отсортированных списка, чтобы идентифицировать те элементы, которые не являютсяобщее для обоих наборов:

eq_set(Xs,Ys) :-
  sort(Xs,X1),
  sort(Ys,Y1),
  X1 == Y1.
...