Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subsets in Prolog

I'm looking for a predicate that works as this:

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

I've seen some subset implementations, but they all work when you want to check if one list is a subset of the another, not when you want to generate the subsets. Any ideas?

like image 595
arubox Avatar asked Feb 06 '11 11:02

arubox


People also ask

How do you find the nth element of a list in Prolog?

Give the definition of nth which returns the nth element of a list. For example nth(X,3,[a,b,c,d,e]) returns X = c. element_at(X,[X|_],1). element_at(X,[_|L],K) :- element_at(X,L,K1), K is K1 + 1.

What does [] mean in Prolog?

[] is an empty list. Note that [x|y] is different than both of these. Also, since x and y start with lower case, they ate atoms, not variables. [X|Y] is a list of one or more elements. So a single element list will match this and match [_] , but a two element list will only match the former.

What is the sum of all elements in a list of integers Prolog?

To sum the elements in a list inductively, we add the first element to the sum of the remaining ones. In Prolog we say this: sum([H|T], S) :- sum(T,X), S is H + X.


2 Answers

Here goes an implementation:

subset([], []).
subset([E|Tail], [E|NTail]):-
  subset(Tail, NTail).
subset([_|Tail], NTail):-
  subset(Tail, NTail).

It will generate all the subsets, though not in the order shown on your example.

As per commenter request here goes an explanation:

The first clause is the base case. It states that the empty list is a subset of the empty list.

The second and third clauses deal with recursion. The second clause states that if two lists have the same Head and the tail of the right list is a subset of the tail of the left list, then the right list is a subset of the left list.

The third clause states that if we skip the head of the left list, and the right list is a subset of the tail of the left list, then the right list is a subset of the left list.

The procedure shown above generates ordered sets. For unordered sets you might use permutation/3:

unordered_subset(Set, SubSet):-
  length(Set, LSet),
  between(0,LSet, LSubSet),
  length(NSubSet, LSubSet),
  permutation(SubSet, NSubSet),
  subset(Set, NSubSet).
like image 84
gusbro Avatar answered Sep 28 '22 05:09

gusbro


On http://www.probp.com/publib/listut.html you will find an implementation of a predicate called subseq0 that does what you want to:

subseq0(List, List).
subseq0(List, Rest) :-
   subseq1(List, Rest).

subseq1([_|Tail], Rest) :-
   subseq0(Tail, Rest).
subseq1([Head|Tail], [Head|Rest]) :-
   subseq1(Tail, Rest).

A short explanation: subseq0(X, Y) checks whether Y is a subset subsequence of X, while subseq1(X, Y) checks whether Y is a proper subset subsequence of X.

Since the default representation of a set is a list with unique elements, you can use it to get all subsets as in the following example:

?- subseq0([1,2,3], X).
X = [1, 2, 3] ;
X = [2, 3] ;
X = [3] ;
X = [] ;
X = [2] ;
X = [1, 3] ;
X = [1] ;
X = [1, 2] ;
false.
like image 24
Nubok Avatar answered Sep 28 '22 05:09

Nubok