Re: Sorting a list of lists in the order of ascending length

"Norbert E. Fuchs" <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
On 24 Sep 2013, at 18:39 , Norbert E. Fuchs <[email protected]> wrote:

> I need all subsets of a list in the order of ascending length of the subsets. 
> 
> Thus I wrote a predicate 
> 
>  generate_subset(+List, -SubList) that generates a SubList of List 
> 
> and that is also used for other purposes, and a variant of insertion sort
> 
>  sort_list_of_lists_in_ascending_length(+ListOfLists, -SortedListOfList) that sorts ListOfLists into SortedListOfList 
> 
> and then called 
> 
>  findall(SubList, generate_subset(List, SubList), SubLists), sort_list_of_lists_in_ascending_length(SubLists, SortedSubLists)
> 
> This works quite nicely and efficiently, but nevertheless I wonder whether there isn't a simpler way to perform this operation. 

I would like to thank Paulo, Richard, Jürgen and Markus for their suggestions below.

Best.

   --- nef


On 24 Sep 2013, at 19:50 , Paulo Moura <[email protected]> wrote:

> An alternative would be to modify an implementation of a power set predicate to return each subset in the form Length-Subset and then to use keysort/2.

Right, but I as I wrote I need generate_subset/2 also for other purposes.


On 25 Sep 2013, at 2:18 , Richard A. O'Keefe <[email protected]> wrote:

> Since 26 July 2007,
> 
> %   Enumerate subsets such that if A subset-of B subset-of S
> %   then    subset_biggest_first(S, B) will be reported
> %   before  subset_biggest_first(S, A).
> 
> subset_biggest_first([], []).
> subset_biggest_first([X|Xs], S) :-
>    subset_biggest_first(Xs, T),
>    ( S = [X|T] ; S = T ).
> 
> %   Enumerate subsets such that if A subset-of B subset-of S
> %   then    subset_smallest_first(S, A) will be reported
> %   before  subset_smallest_first(S, B).
> 
> subset_smallest_first([], []).
> subset_smallest_first([X|Xs], S) :-
>    subset_smallest_first(Xs, T),
>    ( S = T ; S = [X|T] ).
> 
> However, subset_smallest_first/2 satisfies a weaker property:
>  - if S1 is a proper subset of S2, S1 will be generated before S2.
> 
> ?- subset_smallest_first([a,b,c], S).
> S = [] ;
> S = [a] ;
> S = [b] ;
> S = [a, b] ;
> S = [c] ;
> S = [a, c] ;
> S = [b, c] ;
> S = [a, b, c]
> 
> I wonder if this property is strong enough for your needs?

No, since I need the subsets strictly in ascending order of length. (I am working on a theorem prover and I want to find the minimal sets of axioms needed to prove a theorem.)

> Otherwise, something like
> 
> subsets_in_size_order(Set, Sub) :-
>    length(Set, N),
>    between(0, N, L),
>    length(Sub, L),
>    generate_subset(Sub, Set).
> 
> generate_subset([], _).
> generate_subset([X|Xs], Set) :-
>    generate_subset_aux(Set, X, Set1),
>    generate_subset(Xs, Set1).
> 
> generate_subset_aux([X|Set1], X, Set1).
> generate_subset_aux([_|Set],  X, Set1) :-
>    generate_subset_aux(Set,  X, Set1).
> 
> ?- subsets_in_size_order([a,b,c], S).  
> 
> S = [] ;
> S = [a] ;
> S = [b] ;
> S = [c] ;
> S = [a, b] ;
> S = [a, c] ;
> S = [b, c] ;
> S = [a, b, c] 

This will do. I like the elegant subsets_in_size_order/2 while Richard's generate_subset/2 seems to be more complicated than my generate_subset/2 which is

generate_subset([], []).

generate_subset([X|Xs], [X|Ys]):-
  generate_subset(Xs, Ys).
  
generate_subset([_X|Xs], Ys):-
  generate_subset(Xs, Ys).


On 25 Sep 2013, at 8:35 , J. Cleve <[email protected]> wrote:

> Here is a solution, which only needs findall and length.
> Please do NOT discuss the complexity ... ;-)
> 
> 
> numbr(Max,Max,Max) :- !.
> numbr(Min,_Max,Min).
> numbr(Min, Max, N) :- Min1 is Min+1, numbr(Min1,Max,N).
> 
> subset([],_,N) :- N=0,!.
> subset([H|T],[H|L],N) :- N1 is N-1, subset(T,L,N1).
> subset(L2,[_|L],N) :- subset(L2,L,N).
> 
> ?- List=[1,2,3,4,5], length(List,LL), findall(X,
> (numbr(0,LL,N),subset(X,List,N)),L),write(L),nl.

As I wrote, I need subset/2 by itself, and your subset/3 seems to loop if not constrained by numbr/3.


On 25 Sep 2013, at 14:40 , Markus Triska <[email protected]> wrote:

> What about:
> 
>   set_ascending_length_subset(Set, Sub) :-
>           length(Set, N),
>           between(0, N, L),
>           length(Sub, L),
>           phrase(subset(Set), Sub).
> 
>   subset([])     --> [].
>   subset([L|Ls]) --> ( [L] ; []), subset(Ls).
> 
> Example:
> 
>   ?- findall(Sub, set_ascending_length_subset([a,b,c], Sub), Subs).
>   %@ Subs = [[], [a], [b], [c], [a, b], [a, c], [b, c], [a|...]].

This is a very compact an elegant solution.

set_ascending_length_subset/2 is basically the same as Richard's subsets_in_size_order/2, while the DCG for subset/1 when expanded is basically my code for generate_subset/2.


Now instead of my clumsy sort_list_of_lists_in_ascending_length/2 (based on insertion_sort)

sort_list_of_lists_in_ascending_length([], []).

sort_list_of_lists_in_ascending_length([List|Lists], Sorted) :-
  sort_list_of_lists_in_ascending_length(Lists, IntermediateSorted),
  insert(List, IntermediateSorted, Sorted).

insert(X, [], [X]).
insert(X, [Y|Ys], [Y|Zs]) :-
  length(X, LX),
  length(Y, LY),
  LX > LY,
  insert(X, Ys, Zs).
  
insert(X, [Y|Ys], [X,Y|Ys]) :-
  length(X, LX),
  length(Y, LY),
  LX =< LY.

I will used Richard's subsets_in_size_order/2, respectively Markus' set_ascending_length_subset/2, and continue to use my generate_subset/2. Thus instead of the two-step approach

  findall(findall(SubList, generate_subset(List, SubList), SubLists), sort_list_of_lists_in_ascending_length(SubLists, SortedSubLists)

I will simply have 

  findall(SubList, subsets_in_size_order(List, SubList), SortedSubLists)

Thanks again to all of you. Sorry for the long message.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.