In SWI-Prolog their is the predicate sort/2 to sort lists.
Is their a good way to sort a list of list with fixed length by some index. I mean in example, if I have the following list of elements
[[1,2], [3,1], [2, 5]]
Is their a function in SWI-Prolog to sort it by first or second index. By first index the result would be:
[[1,2], [2,5], [3, 1]]
By second index:
[[3,1], [1,2], [2, 5]]
The efficient and portable way to get what you want goes like this:
map each element X of the original list to a pair I-X, where I is the value to sort by.
use keysort/2 on the resulting list; note that keysort/2 preserves duplicate items.
at last, get rid of the values of I in the sorted list—we don't need them anymore.
The above directly translates to the predicate lists_sortedBySecondItem/2:
lists_sortedBySecondItem(Ess,Xss) :-
maplist(list_pair,Ess,Pairs), % (1)
keysort(Pairs,Sorted), % (2)
maplist(pair_second,Sorted,Xss). % (3)
list_pair(Xs,I-Xs) :-
nth(2,Xs,I).
pair_second(_-X,X).
Sample query using GNU Prolog 1.5.0:
| ?- lists_sortedBySecondItem([[1,2],[3,1],[2,5]], Xss).
Xss = [[3,1],[1,2],[2,5]]
yes
Can use https://www.swi-prolog.org/pldoc/doc_for?object=predsort/3 if removing duplicates is OK:
test(Nth1, S) :-
L = [[1,2], [3,1], [2, 5], [1,2]],
predsort(nth1_compare(Nth1), L, S).
nth1_compare(Nth1, Comp, L1, L2) :-
nth1(Nth1, L1, V1),
nth1(Nth1, L2, V2),
compare(Comp, V1, V2).
Results:
?- test(1, S).
S = [[1,2],[2,5],[3,1]].
?- test(2, S).
S = [[3,1],[1,2],[2,5]].
Related
I am using Prolog to try and check if a list can be split into 2 sublists(subarrays) that have equal sums.
The following should succeed: [1,2,3,6], [2,1,1], [0], [1,1,2]
The following should fail: [1,4,8], [1,3,2], [2,2,1,1]
I believe my program is creating subsequences instead of sublists. This is causing queries similar to [1,3,2] and [2,2,1,1] to succeed when they should fail.
In the example of the query [1,3,2] it is returning true because the subsequences [1,2] and [3] have equal sums. That should not be allowed. Instead, [1,3,2] should be split into sublists [1]/[3,2] and [1,3]/[2]. Hence, it should fail.
I am unsure how to modify the subL predicate to return sublists instead of subsequences.
Here is what I have so far:
split([]).
split([0]).
split([H|T]) :-
subL([H|T], LEFT, RIGHT),
sum(LEFT, SUM1),
sum(RIGHT, SUM2),
SUM1=SUM2.
subL([],[],[]).
subL([H|T], [H|T2], X) :-
subL(T, T2, X).
subL([H|T], X, [H|T2]) :-
subL(T, X, T2).
sum([H|T], SUM1) :-
sum(T, SUM2),
SUM1 is SUM2 + H.
sum([H], SUM1) :-
H = SUM1.
Any help with this would be greatly appreciated. Thank you
YOu can make use of append to split the list into different lists. Indeed:
?- append(L, R, [1,2,3,6]).
L = [],
R = [1, 2, 3, 6] ;
L = [1],
R = [2, 3, 6] ;
L = [1, 2],
R = [3, 6] ;
L = [1, 2, 3],
R = [6] ;
L = [1, 2, 3, 6],
R = [] ;
false.
so you can write a predicate:
split(X) :-
append(L, R, X),
sum(L, S),
sum(R, S).
Here we thus check if both the left and the right sublist sum up to the same sum S. You however slighly need to change your sum/2 predicate such that the sum for an empty list is 0 as well. I leave that as an exercise.
The above is not very efficient, since it takes O(n2) time. You can make it linear by first calculating the sum of the entire list, and then make a predicate that iterates over the list, each time keeping track of the sum of the elements on the left side, and the remaining sum on the right side. I think that by first solving it the "naive" way, you likely will find it easier to implement that as an improvement.
% appends an element to the beginning of a list.
append_element(X, T, [X|T]).
% append a list to another list to create a combined list,
% by breaking the first list apart, and using append_element.
append_list([], L, L).
append_list([H|T], L, NewList) :-
append_element(H, L, NL),
append_list(T, NL, NL).
When I try to run append_list,
?- append_list([1,2], [3, 4, 5], NL).
I get back false. Instead of
NL = [2, 1, 3, 4, 5].
Why?
I have this list :
C = [[1,0],[2,3],[1,2],[1,3]]
I'll like find if the number 1 included in a sublist inside my list in position [1,_ ] and i like to save to a list Newlist the number of X ..... [1,X].
I will give an example... i have the list C and i am searching for sublist which first element it's 1 and give me the Newlist.
The Newlist must be : Newlist=[0,2,3]
It had the second element of the sublists who has the number 1 at the first element.
If you use SWI-Prolog with module lambda.pl, (you can find it at http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl) you can write
:- use_module(library(lambda)).
my_filter(V, L, R) :-
foldl(V+\X^Y^Z^(X = [V,W]
-> append(Y, [W], Z)
; Z = Y),
L, [], R).
nth0/3 allows to access list' elements by index:
?- C = [[1,0],[2,3],[1,2],[1,3]], findall(P, nth0(P, C, [1,_]), NewList).
C = [[1, 0], [2, 3], [1, 2], [1, 3]],
NewList = [0, 2, 3].
edit I'm sorry I didn't read the question right. nth0 is misleading. Could be instead
findall(E, member([1,E], C), NewList)
You need a "filter". This is what it could look like:
filter_1_at_pos_1([], []). % The new list is empty when the input list is empty
filter_1_at_pos_1([[1,X]|Sublist], [X|Xs]) :- % The first element is 1 so the
% second element belongs to the
% new list
!, filter_1_at_pos_1(Sublist, Xs). % filter the remainder of the list
filter_1_at_pos_1([[N,_]|Sublist], Xs) :-
N \== 1, % The first element is not 1, ignore the second element
!, filter_1_at_pos_1(Sublist, Xs).
As #mbratch suggested, just define the solution for one element of the input list for each possible condition, in this case 1) empty list 2) first element is 1 and 3) first element is not 1.
?- C = [[1,0],[2,3],[1,2],[1,3]], filter_1_at_pos_1(C, NewList).
C = [[1, 0], [2, 3], [1, 2], [1, 3]],
NewList = [0, 2, 3].
The cuts make the predicate deterministic. The cut in the last clause is not necessary.
I want to write a prolog predicate with the following output:
?- all_match([1,2,3,2,3,1,2],L).
L = [[], [1], [1, 2], [2], [2, 3], [3]].
?- all_match([1,1,1,2],L).
L = [[], [1], [1, 1]].
The purpose is to find the sublists that repeat more than once.
So far I found the solution to find all sublists in a list-
subSet(_, []).
subSet(L, [S|T]) :- append(_, L2,L), append([S|T], _, L2).
But I can't figure out how to repeat the search for every element.
Thanks in advance.
This code is a little different from your requirements, in that all_match/2 will omit the empty sequence and fail if there where no repeated subsequences in the input.
repeated(List, Sublist) :-
% For all prefixes, suffixes:
append(Sublist, Tail, List), Sublist \= [],
% For all suffixes of the former suffixes:
append(_, TailTail, Tail),
% Is the head of the latter suffix equal to the head of the input?
append(Sublist, _, TailTail).
repeated([_|List], Sublist) :-
% Strip leading character and continue
repeated(List, Sublist).
all_match(List, Lists) :-
% Aggregate all repeated sequences or fail if there weren't any.
setof(L, repeated(List, L), Lists).
A sketch of the idea of the first clause of repeated/2:
|----------------List------------------| repeated(List, Sublist)
|--Sublist--|------------Tail----------| append(Sublist, Tail, List)
|--Sublist--| |-----TailTail-----| append(_, TailTail, Tail)
|--Sublist--| |--Sublist--| | append(Sublist, _, TailTail)
Result:
?- all_match([1,2,3,2,3,1,2],L).
L = [[1], [1, 2], [2], [2, 3], [3]].
Update to allow overlapping sequences:
repeated([H|List], Sublist) :-
append(Sublist, _, [H|List]), Sublist \= [],
append(_, Tail, List),
append(Sublist, _, Tail).
repeated([_|List], Sublist) :-
repeated(List, Sublist).
I like Kay's answer (+1). Here a variation on thema
all_match(L, M) :-
take(L, M, R),
take(R, M, _).
take(L, [A|B], R) :- % use [A|B] to remove empties
append(_, T, L),
append([A|B], R, T).
yields
?- setof(L,all_match([1,2,3,2,3,1,2],L),R).
R = [[1], [1, 2], [2], [2, 3], [3]].
I need some help writing a predicate in Prolog that, given a number as input, returns a list of lists with numbers that add up to it.
Let's call the predicate addUpList/2, it should work like this:
?- addUpList(3,P).
P = [[1,2], [2,1], [1,1,1]]. % expected result
I'm having so much trouble figuring this out I'm beginning to think it's impossible. Any ideas? Thanks in advance.
Try this:
condense([], Rs, Rs).
condense([X|Xs], Ys, Zs) :-
condense(Xs, [X|Ys], Zs).
condense([X, Y|Xs], Ys, Zs) :-
Z is X + Y,
condense([Z|Xs], Ys, Zs).
condense(Xs, Rs) :-
condense(Xs, [], Rs).
expand(0, []).
expand(N, [1|Ns]) :-
N > 0,
N1 is N - 1,
expand(N1, Ns).
addUpList(N, Zs) :-
expand(N, Xs),
findall(Ys, condense(Xs, Ys), Zs).
Let me know what marks I get. :-)
The rule num_split/2 generates ways of splitting a number into a list, where the first element X is any number between 1 and N and the rest of the list is a split of N-X.
num_split(0, []).
num_split(N, [X | List]) :-
between(1, N, X),
plus(X, Y, N),
num_split(Y, List).
In order to get all such splits, just call findall/3 on num_split/2.
add_up_list(N, Splits) :-
findall(Split, num_split(N, Split), Splits).
Usage example:
?- add_up_list(4, Splits).
Splits =
[[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]].
See also the post by #hardmath which gives the same answer with a bit more explanation.
The example given in the Question suggests that compositions (ordered partitions) of any positive integer N ≤ 10 are wanted. Note however that the solution [3] for N=3 seems to have been omitted/overlooked. The number of compositions of N is 2^(N-1), so N=10 gives a long list but not an unmanageable one.
It is also desired to collect all such solutions into a list, something that findall/3 can do generically after we write a predicate composition/2 that generates them.
The idea is to pick the first summand, anything between 1 and N, subtract it from the total and recurse (stopping with an empty list when the total reaches zero). SWI-Prolog provides a predicate between/3 that can generate those possible first summands, and Amzi! Prolog provides a similar predicate for/4. For the sake of portability we write our own version here.
summand(Low,High,_) :-
Low > High,
!,
fail.
summand(Low,High,Low).
summand(Low,High,Val) :-
Now is Low + 1,
summand(Now,High,Val).
composition(0,[ ]).
composition(N,[H|T]) :-
summand(1,N,H),
M is N - H,
composition(M,T).
Given the above Prolog source code, compiled or interpreted, a list of all solutions can be had in this way:
?- findall(C,composition(3,C),L).
C = H126
L = [[1, 1, 1], [1, 2], [2, 1], [3]]
Of course some arrangement of such a list of solutions or the omission of the singleton list might be required for your specific application, but this isn't clear as the Question is currently worded.
There are plenty of great answers to this question already, but here is another solution to this problem for you to consider. This program differs from the others in that it is very efficient, and generates non-redundant solutions of lists which are assumed to represent sets of integers which add up to the specified number.
gen(N, L) :-
gen(N-1, N, N, FL),
dup_n(FL, L).
gen(C-F, M, M, [C-F]).
gen(C-F, S, M, [C-F|R]) :-
S < M, C > 1,
C0 is C - 1,
F0 is floor(M / C0),
S0 is S + (C0 * F0),
gen(C0-F0, S0, M, R).
gen(C-F, S, M, R) :-
F > 0,
F0 is F - 1,
S0 is S - C,
gen(C-F0, S0, M, R).
dup_n([], []).
dup_n([_-0|R], L) :-
!, dup_n(R, L).
dup_n([V-F|R], [V|L]) :-
F0 is F - 1,
dup_n([V-F0|R], L).
Your implementation of addUpList/2 can be achieved by:
addUpList(N, P) :-
findall(L, gen(N, L), P).
Which should give you the following behaviour:
?- addUpList(4,L).
L = [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]].
Note that the list containing one 2 and two 1s only appears once in this result set; this is because gen/4 computes unique sets of integers which add up to the specified number.
This answer is somewhere between
#Kaarel's answer and
#sharky's "efficient" answer.
Like #sharky's code, we impose an ordering relation between adjacent list items to restrict the size of the solution space---knowing how to inflate it if we ever need to. So the solution sets of break_down/2 and gen/2 by #sharky are equal (disregarding list reversal).
And as for performance, consider:
?- time((break_down(40,_),false)).
% 861,232 inferences, 0.066 CPU in 0.066 seconds (100% CPU, 13127147 Lips)
false.
?- time((gen(40,_),false)).
% 8,580,839 inferences, 0.842 CPU in 0.842 seconds (100% CPU, 10185807 Lips)
false.