How do I skip an element that already have been printing prolog - prolog

Im trying to do an exercise in Prolog like this: i introduce this list [10,20, 10, 20, 30], and the program shows :
10 - 2 time; 20 - 2 times; 30 - 1 times.
Here is my code:
conta(_,[], 0).
conta(X, [X|T], N) :- conta(X,T, N2), N is N2 + 1 .
conta(X, [Y|T], N) :- X \= Y, conta(X,T,N).
aux([],[]).
aux([X|L],L1):-conta(X,L1,C),write(X),write(C), write('vezes'),aux(L,L1).
But the result is this:
10 - 2times 20 -2time 10-2times 20-2times 30-1 time
false.
He shows the element the number of time that the element is in the list.
Any help, Please!!

In your problem statement you are interspersing the pure relation with side-effects. While you might solve the problem in this manner you will see only very few of Prolog's interesting properties. Instead, try to formulate your problem as a pure relation. So imagine, you have it already implemented, and formulate some queries for it:
?- list_vezes([10,20, 10, 20, 30], [10-2,20-2,30-1]).
true.
The following solution counts and removes the corresponding elements. It has some n2 runtime.
list_vezes([], []).
list_vezes([E|Es1], [E-N|Vezes]) :-
n_occ(E, Es1,Es2, 1,N),
list_vezes(Es2, Vezes).
n_occ(_, [],[], N,N).
n_occ(E, [E|Es0],Es, N0,N) :-
N1 is N0+1,
n_occ(E, Es0,Es, N1,N).
n_occ(E, [F|Es0],[F|Es], N0,N) :-
dif(F,E),
n_occ(E, Es0,Es, N0,N).
In many Prolog systems prolog-dif is a built-in. See the link what to do if you do not have it.
Now, if you still want to print out the text you said, you might do this with this new list:
printitem(E-N) :-
writeq(E-N), write(' times\n').
compter_et_imprimer(L) :-
list_vezes(L, Vezes),
maplist(printitem, Vezes).

Related

How to improve this code that looks for a specific number in a list?

I'm writing prolog code that finds a certain number; a number is the right number if it's between 0 and 9 and not present in a given list. To do this I wrote a predicate number/3 that has the possible numbers as the first argument, the list in which the Rightnumber cannot be present and the mystery RightNumber as third argument:
number([XH|XT], [H|T], RightNumber):-
member(XH, [H|T]), !,
number(XT, [H|T], RightNumber).
number([XH|_], [H|T], XH):-
\+ member(XH, [H|T]).
so this code basically says that if the Head of the possible numbers list is already a member of the second list, to cut of the head and continue in recursion with the tail.
If the element is not present in the second list, the second clause triggers and tells prolog that that number is the RightNumber. It's okay that it only gives the first number that is possible, that's how I want to use it.
This code works in theory, but I was wondering if there's a better way to write it down? I'm using this predicate in another predicate later on in my code and it doesn't work as part of that. I think it's only reading the first clause, not the second and fails as a result.
Does anybody have an idea that might improve my code?
sample queries:
?- number([0,1,2,3,4,5,6,7,8,9], [1,2], X).
X = 3
?- number([0,1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,0], X).
X = 9
First, the code does not work. Consider:
?- number(Xs, Ys, N).
nontermination
This is obviously bad: For this so-called most general query, we expect to obtain answers, but Prolog does not give us any answer with this program!
So, I first suggest you eliminate all impurities from your program, and focus on a clean declarative description of what you want.
I give you a start:
good_number(N, Ls) :-
N in 0..9,
maplist(#\=(N), Ls).
This states that the relation is true if N is between 0 and 9, and N is different from any integer in Ls. See clpfd for more information about CLP(FD) constraints.
Importantly, this works in all directions. For example:
?- good_number(4, [1,2,3]).
true.
?- good_number(11, [1,2,3]).
false.
?- good_number(N, [1,2,3]).
N in 0\/4..9.
And also in the most general case:
?- good_number(N, Ls).
Ls = [],
N in 0..9 ;
Ls = [_2540],
N in 0..9,
N#\=_2540 ;
Ls = [_2750, _2756],
N in 0..9,
N#\=_2756,
N#\=_2750 .
This, with only two lines of code, we have implemented a very general relation.
Also see logical-purity for more information.
First of all, your predicate does not work, nor does it check all the required constraints (between 0 and 9 for instance).
Several things:
you unpack the second list [H|T], but you re-pack it when you call member(XH, [H|T]); instead you can use a list L (this however slightly alters the semantics of the predicate, but is more accurate towards the description);
you check twice member/2ship;
you do not check whether the value is a number between 0 and 9 (and an integer anyway).
A better approach is to construct a simple clause:
number(Ns, L, Number) :-
member(Number, Ns),
integer(Number),
0 =< Number,
Number =< 9,
\+ member(Number, L).
A problem that remains is that Number can be a variable. In that case integer(Number) will fail. In logic we would however expect that Prolog unifies it with a number. We can achieve this by using the between/3 predicate:
number(Ns, L, Number) :-
member(Number, Ns),
between(0, 9, Number),
\+ member(Number, L).
We can also use the Constraint Logic Programming over Finite Domains library and use the in/2 predicate:
:- use_module(library(clpfd)).
number(Ns, L, Number) :-
member(Number, Ns),
Number in 0..9,
\+ member(Number, L).
There are still other things that can go wrong. For instance we check non-membership with \+ member(Number, L). but in case L is not grounded, this will fail, instead of suggesting lists where none of the elements is equal to Number, we can use the meta-predicate maplist to construct lists and then call a predicate over every element. The predicate we want to call over every element is that that element is not equal to Number, so we can use:
:- use_module(library(clpfd)).
number(Ns, L, Number) :-
member(Number, Ns),
Number in 0..9,
maplist(#\=(Number), L).

How to assert new data dynamically in Prolog

I have previously define a few facts dynamically as below.
% declare dynamic facts
:- dynamic title/2.
:- dynamic author/2.
:- dynamic publisher/2.
:- dynamic price/2.
:- dynamic call_number/2.
:- dynamic edition/2.
:- dynamic data_disk/2.
and assert these facts every time the program runs
:- assert(title(book1, 'Elementary Statistics')).
:- assert(title(book2, 'Statistics for Engineers')).
:- assert(title(book3, 'Statistics for Engineers and Scientists')).
:- assert(title(book4, 'IT in Language Learning')).
:- assert(author(book1, 'Patricia Wilson')).
:- assert(author(book2, 'James Mori')).
:- assert(author(book3, 'James Mori')).
:- assert(author(book4, 'O Ivan')).
:- assert(publisher(book1, 'Addison Wesley')).
:- assert(publisher(book2, 'World Scientific')).
:- assert(publisher(book3, 'World Scientific')).
:- assert(publisher(book4, 'Universal Press')).
:- assert(price(book1, 75)).
:- assert(price(book2, 125)).
:- assert(price(book3, 125)).
:- assert(price(book4, 5)).
:- assert(call_number(book1, 'QA373')).
:- assert(call_number(book2, 'QA673')).
:- assert(call_number(book3, 'QA674')).
:- assert(call_number(book4, 'QA007')).
:- assert(edition(book1, 1)).
:- assert(edition(book2, 3)).
:- assert(edition(book3, 2)).
:- assert(edition(book4, 1)).
:- assert(data_disk(book1, 'No')).
:- assert(data_disk(book2, 'Yes')).
:- assert(data_disk(book3, 'Yes')).
:- assert(data_disk(book4, 'No')).
As you can see the facts are in a certain order
book1
book2
book3
book4
How can I get the last X, where X is bookX, and increment by 1 so that the new book to be inserted will always be (X+1)?
You found one solution (i.e., count the existing facts, and add 1), which works but has one major drawback: It makes the run time of adding a single new fact proportional to the number of already asserted facts. This means that asserting a series of N facts takes time proportional to N2.
Ideally, we would like to have a situation where asserting a single fact is in &Oscr;(1), and asserting N facts is thus in &Oscr;(N).
One way to achieve this is to reconsider your initial representation of books.
For example, suppose that you present your books like this (some data omitted for brevity):
book([title('Elementary Statistics'),
author('Patricia Wilson'),
price(75)]).
book([title('Statistics for Engineers'),
author('James Mori'),
publisher('World Scientific')]).
Note that this representation allows us to omit fields that are only present in some of the books. Other representations would also make sense.
We can easily fetch all these facts with findall/3:
?- findall(Book, book(Book), Books).
That's linear in the number of such facts.
Further, let us define assert_book_/3 as follows:
assert_book_(Book, N0, N) :-
memberchk(title(Title), Book),
memberchk(author(Author), Book),
assertz(title(N0,Title)),
assertz(author(N0,Author)),
N #= N0 + 1.
For the sake of example, I am focusing on the title and author. I leave extending this as an exercise.
The arguments of this predicate are:
the book to be asserted, represented as a list of attributes
the current index N0
the next index N1, which is simply one greater than N0.
Now the main point: These arguments are in a suitable order to fold the predicate over a list of books, using the meta-predicate foldl/4:
?- findall(Book, book(Book), Books),
foldl(assert_book_, Books, 1, _).
After running this query, we have:
?- title(N, T).
N = 1,
T = 'Elementary Statistics' ;
N = 2,
T = 'Statistics for Engineers'.
And similar facts for author/2 in the database:
?- author(N, T).
N = 1,
T = 'Patricia Wilson' ;
N = 2,
T = 'James Mori'.
Thus, we have used foldl/4 to implicitly keep track of the running index we need, and achieved a solution that has the desired running time.
Note that there is also a wise-cracking solution for your task:
assert_title(Book, Title) :-
atom_concat(book, N0, Book),
atom_number(N0, N),
assertz(title(N, Title)).
This is obviously not what you looking for, but would work for the example you show, if you use for example:
:- assert_title(book1, 'Elementary Statistics').
:- assert_title(book2, 'Statistics for Engineers').
Now we have again:
?- title(N, Title).
N = 1,
Title = 'Elementary Statistics' ;
N = 2,
Title = 'Statistics for Engineers'.
The joke here is that you have actually entered the running index already, and we can use atom_concat/3 to obtain it:
?- atom_concat(book, N0, book1),
atom_number(N0, N).
N0 = '1',
N = 1.
;-)
I cleared my mind at the nearest Starbucks and came up with the simplest answer.
add_book :-
aggregate_all(count, title(_,_), Count),
NewCount is Count + 1,
atom_concat('book', NewCount, NewBook).
The aggregate_all function will count number of title predicates that's available in my knowledge base and some calculation will be performed.
I am open to better suggestion though, do reply if you have a better approach.

PROLOG: Determining if elements in list are equal if order does not matter

I'm trying to figure out a way to check if two lists are equal regardless of their order of elements.
My first attempt was:
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L).
However, this only checks if all elements of the list on the left exist in the list on the right; meaning areq([1,2,3],[1,2,3,4]) => true. At this point, I need to find a way to be able to test thing in a bi-directional sense. My second attempt was the following:
areq([],[]).
areq([],[_|_]).
areq([H1|T1], L):- member(H1, L), areq(T1, L), append([H1], T1, U), areq(U, L).
Where I would try to rebuild the lest on the left and swap lists in the end; but this failed miserably.
My sense of recursion is extremely poor and simply don't know how to improve it, especially with Prolog. Any hints or suggestions would be appreciated at this point.
As a starting point, let's take the second implementation of equal_elements/2 by #CapelliC:
equal_elements([], []).
equal_elements([X|Xs], Ys) :-
select(X, Ys, Zs),
equal_elements(Xs, Zs).
Above implementation leaves useless choicepoints for queries like this one:
?- equal_elements([1,2,3],[3,2,1]).
true ; % succeeds, but leaves choicepoint
false.
What could we do? We could fix the efficiency issue by using
selectchk/3 instead of
select/3, but by doing so we would lose logical-purity! Can we do better?
We can!
Introducing selectd/3, a logically pure predicate that combines the determinism of selectchk/3 and the purity of select/3. selectd/3 is based on
if_/3 and (=)/3:
selectd(E,[A|As],Bs1) :-
if_(A = E, As = Bs1,
(Bs1 = [A|Bs], selectd(E,As,Bs))).
selectd/3 can be used a drop-in replacement for select/3, so putting it to use is easy!
equal_elementsB([], []).
equal_elementsB([X|Xs], Ys) :-
selectd(X, Ys, Zs),
equal_elementsB(Xs, Zs).
Let's see it in action!
?- equal_elementsB([1,2,3],[3,2,1]).
true. % succeeds deterministically
?- equal_elementsB([1,2,3],[A,B,C]), C=3,B=2,A=1.
A = 1, B = 2, C = 3 ; % still logically pure
false.
Edit 2015-05-14
The OP wasn't specific if the predicate
should enforce that items occur on both sides with
the same multiplicities.
equal_elementsB/2 does it like that, as shown by these two queries:
?- equal_elementsB([1,2,3,2,3],[3,3,2,1,2]).
true.
?- equal_elementsB([1,2,3,2,3],[3,3,2,1,2,3]).
false.
If we wanted the second query to succeed, we could relax the definition in a logically pure way by using meta-predicate
tfilter/3 and
reified inequality dif/3:
equal_elementsC([],[]).
equal_elementsC([X|Xs],Ys2) :-
selectd(X,Ys2,Ys1),
tfilter(dif(X),Ys1,Ys0),
tfilter(dif(X),Xs ,Xs0),
equal_elementsC(Xs0,Ys0).
Let's run two queries like the ones above, this time using equal_elementsC/2:
?- equal_elementsC([1,2,3,2,3],[3,3,2,1,2]).
true.
?- equal_elementsC([1,2,3,2,3],[3,3,2,1,2,3]).
true.
Edit 2015-05-17
As it is, equal_elementsB/2 does not universally terminate in cases like the following:
?- equal_elementsB([],Xs), false. % terminates universally
false.
?- equal_elementsB([_],Xs), false. % gives a single answer, but ...
%%% wait forever % ... does not terminate universally
If we flip the first and second argument, however, we get termination!
?- equal_elementsB(Xs,[]), false. % terminates universally
false.
?- equal_elementsB(Xs,[_]), false. % terminates universally
false.
Inspired by an answer given by #AmiTavory, we can improve the implementation of equal_elementsB/2 by "sharpening" the solution set like so:
equal_elementsBB(Xs,Ys) :-
same_length(Xs,Ys),
equal_elementsB(Xs,Ys).
To check if non-termination is gone, we put queries using both predicates head to head:
?- equal_elementsB([_],Xs), false.
%%% wait forever % does not terminate universally
?- equal_elementsBB([_],Xs), false.
false. % terminates universally
Note that the same "trick" does not work with equal_elementsC/2,
because of the size of solution set is infinite (for all but the most trivial instances of interest).
A simple solution using the sort/2 ISO standard built-in predicate, assuming that neither list contains duplicated elements:
equal_elements(List1, List2) :-
sort(List1, Sorted1),
sort(List2, Sorted2),
Sorted1 == Sorted2.
Some sample queries:
| ?- equal_elements([1,2,3],[1,2,3,4]).
no
| ?- equal_elements([1,2,3],[3,1,2]).
yes
| ?- equal_elements([a(X),a(Y),a(Z)],[a(1),a(2),a(3)]).
no
| ?- equal_elements([a(X),a(Y),a(Z)],[a(Z),a(X),a(Y)]).
yes
In Prolog you often can do exactly what you say
areq([],_).
areq([H1|T1], L):- member(H1, L), areq(T1, L).
bi_areq(L1, L2) :- areq(L1, L2), areq(L2, L1).
Rename if necessary.
a compact form:
member_(Ys, X) :- member(X, Ys).
equal_elements(Xs, Xs) :- maplist(member_(Ys), Xs).
but, using member/2 seems inefficient, and leave space to ambiguity about duplicates (on both sides). Instead, I would use select/3
?- [user].
equal_elements([], []).
equal_elements([X|Xs], Ys) :-
select(X, Ys, Zs),
equal_elements(Xs, Zs).
^D here
1 ?- equal_elements(X, [1,2,3]).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
2 ?- equal_elements([1,2,3,3], [1,2,3]).
false.
or, better,
equal_elements(Xs, Ys) :- permutation(Xs, Ys).
The other answers are all elegant (way above my own Prolog level), but it struck me that the question stated
efficient for the regular uses.
The accepted answer is O(max(|A| log(|A|), |B|log(|B|)), irrespective of whether the lists are equal (up to permutation) or not.
At the very least, it would pay to check the lengths before bothering to sort, which would decrease the runtime to something linear in the lengths of the lists in the case where they are not of equal length.
Expanding this, it is not difficult to modify the solution so that its runtime is effectively linear in the general case where the lists are not equal (up to permutation), using random digests.
Suppose we define
digest(L, D) :- digest(L, 1, D).
digest([], D, D) :- !.
digest([H|T], Acc, D) :-
term_hash(H, TH),
NewAcc is mod(Acc * TH, 1610612741),
digest(T, NewAcc, D).
This is the Prolog version of the mathematical function Prod_i h(a_i) | p, where h is the hash, and p is a prime. It effectively maps each list to a random (in the hashing sense) value in the range 0, ...., p - 1 (in the above, p is the large prime 1610612741).
We can now check if two lists have the same digest:
same_digests(A, B) :-
digest(A, DA),
digest(B, DB),
DA =:= DB.
If two lists have different digests, they cannot be equal. If two lists have the same digest, then there is a tiny chance that they are unequal, but this still needs to be checked. For this case I shamelessly stole Paulo Moura's excellent answer.
The final code is this:
equal_elements(A, B) :-
same_digests(A, B),
sort(A, SortedA),
sort(B, SortedB),
SortedA == SortedB.
same_digests(A, B) :-
digest(A, DA),
digest(B, DB),
DA =:= DB.
digest(L, D) :- digest(L, 1, D).
digest([], D, D) :- !.
digest([H|T], Acc, D) :-
term_hash(H, TH),
NewAcc is mod(Acc * TH, 1610612741),
digest(T, NewAcc, D).
One possibility, inspired on qsort:
split(_,[],[],[],[]) :- !.
split(X,[H|Q],S,E,G) :-
compare(R,X,H),
split(R,X,[H|Q],S,E,G).
split(<,X,[H|Q],[H|S],E,G) :-
split(X,Q,S,E,G).
split(=,X,[X|Q],S,[X|E],G) :-
split(X,Q,S,E,G).
split(>,X,[H|Q],S,E,[H|G]) :-
split(X,Q,S,E,G).
cmp([],[]).
cmp([H|Q],L2) :-
split(H,Q,S1,E1,G1),
split(H,L2,S2,[H|E1],G2),
cmp(S1,S2),
cmp(G1,G2).
A simple solution using cut.
areq(A,A):-!.
areq([A|B],[C|D]):-areq(A,C,D,E),areq(B,E).
areq(A,A,B,B):-!.
areq(A,B,[C|D],[B|E]):-areq(A,C,D,E).
Some sample queries:
?- areq([],[]).
true.
?- areq([1],[]).
false.
?- areq([],[1]).
false.
?- areq([1,2,3],[3,2,1]).
true.
?- areq([1,1,2,2],[2,1,2,1]).
true.

Sorting large lists in Prolog: Not enough memory

I'm trying to sort a 10k element list in prolog with bubblesort and I get the out of local stack error. Mergesort seems to be the best option since I don't get any errors for the same input. However I'd really like to get some running times for bubblesort with large input data but I can't. Any ideas?
Here's the code:
%% NOTE: SWI-PROLOG USED
%% generate_list(Limit, N, L): - insert upper limit and length of list N
%% to get a random list with N numbers from 0 to limit
generate_list(_, 0, []).
generate_list(Limit, N, [Y|L]):-
N =\= 0,
random(0, Limit, Y),
N1 is N-1,
generate_list(Limit, N1, L).
%% bubble(L, Ls, max):- insert list L and get max member of list by
%% swapping members from the start of L.
bubble([Z], [], Z).
bubble([X,Y|L], [X|Ls], Z):- X =< Y, bubble([Y|L], Ls, Z).
bubble([X,Y|L], [Y|Ls], Z):- X > Y, bubble([X|L], Ls, Z).
%% bubble_sort(List, Accumulator, Sorted_List)
bubblesort([X], Ls, [X|Ls]).
bubblesort(L, Accumulate, Result):- bubble(L, Ls, Max),
bubblesort(Ls, [Max|Accumulate], Result).
bubble_sort(L, Sorted):- bubblesort(L, [], Sorted).
As you can I see I'm using tail recursion. I've also tried enlarging the stacks by using:
set_prolog_stack(global, limit(100 000 000 000)).
set_prolog_stack(trail, limit(20 000 000 000)).
set_prolog_stack(local, limit(2 000 000 000)).
but it just runs for a bit longer. Eventually I get out of local stack again.
Should I use another language like C and malloc the list or not use recursion?
Since there are two answers, and no one pointed out explicitly enough the reason why you get into "out of local stack" trouble (Mat says in the comment to your question that your predicates are not deterministic, but does not explain exactly why).
Two of the predicates you have defined, namely, bubblesort/3 and bubble/3, have mutually exclusive clauses. But Prolog (at least SWI-Prolog) does not recognize that these are mutually exclusive. So, choice points are created, you don't get tail recursion optimization, and probably no garbage collection (you need to measure using your implementation of choice if you want to know how much goes where and when).
You have two different problems.
Problem 1: lists with exactly one element
This problem pops up in both predicates. In the most simple predicate possible:
foo([_]).
foo([_|T]) :-
foo(T).
And then:
?- foo([a]).
true ;
false.
This is not surprising; consider:
?- [a] = [a|[]].
true.
You can solve this by using a technique called lagging:
bar([H|T]) :-
bar_1(T, H).
bar_1([], _).
bar_1([H|T], _) :-
bar_1(T, H).
Then:
?- bar([a]).
true.
In the definition of bar_1/2, the first argument to the first clause is the empty list; the first argument to the second clause is a non-empty list (a list with at least one element, and a tail). Prolog does not create choice points when all clauses are obviously exclusive. What obvious means will depend on the implementation, but usually, when the first arguments to all clauses are all terms with different functors, then no choice points are created.
Try the following (you might get different results, but the message is the same):
?- functor([], Name, Arity).
Name = [],
Arity = 0.
?- functor([_|_], Name, Arity).
Name = '[|]',
Arity = 2.
See this question and the answer by Mat to see how you can use this to make your program deterministic.
Mat, in his answer, uses this approach, if I see correctly.
Problem 2: constraints (conditions) in the body of the clauses
This is the problem with the second and third clause of bubble/3. In the textbook "correct" example of choosing the minimum of two elements:
min(A, B, B) :- B #< A.
min(A, B, A) :- A #=< B.
Then:
?- min(1,2,1).
true.
but:
?- min(2,1,1).
true ;
false.
You can solve this in two ways: either by doing what Mat is doing, which is, using compare/3, which succeeds deterministically; or, by doing what CapelliC is doing, which is, using an if-then-else.
Mat:
min_m(A, B, Min) :-
compare(Order, A, B),
min_order(Order, A, B, Min).
min_order(<, A, _, A).
min_order(=, A, _, A).
min_order(>, _, B, B).
And Carlo:
min_c(A, B, Min) :-
( B #< A
-> Min = B
; Min = A
).
I know there will always be at least as many opinions as heads, but both are fine, depending on what you are doing.
PS
You could use the built in length/2 to generate a list, and re-write your generate_list/3 like this:
generate_list(Limit, Len, List) :-
length(List, Len),
random_pos_ints(List, Limit).
random_pos_ints([], _).
random_pos_ints([H|T], Limit) :-
random(0, Limit, H),
random_pos_ints(T, Limit).
The helper random_pos_ints/2 is a trivial predicate that can be expressed in terms of maplist:
generate_list(Limit, Len, List) :-
length(List, Len),
maplist(random(0, Limit), List).
Here is a version of bubble/3 that is deterministic if the first argument is instantiated, so that tail call optimisation (and, more specifically, tail recursion optimisation) applies:
bubble([L|Ls0], Ls, Max) :- phrase(bubble_(Ls0, L, Max), Ls).
bubble_([], Max, Max) --> [].
bubble_([L0|Ls0], Max0, Max) -->
elements_max(L0, Max0, Max1),
bubble_(Ls0, Max1, Max).
elements_max(X, Y, Max) -->
{ compare(C, X, Y) },
c_max(C, X, Y, Max).
c_max(<, X, Y, Y) --> [X].
c_max(=, X, Y, Y) --> [X].
c_max(>, X, Y, X) --> [Y].
Example usage, with the rest of the program unchanged (running times depend on the random list, which is bad if you want to reproduce these results - hint: introduce the random seed as argument to fix this):
?- generate_list(100, 10_000, Ls), time(bubble_sort(Ls, Ls1)).
% 200,099,991 inferences, 29.769 CPU in 34.471 seconds
...
For testing different versions, please use a version of the query that you can use to reliably reproduce the same initial list, such as:
?- numlist(1, 10_000, Ls0), time(bubble_sort(Ls0, Ls)).
The nice thing is: If you just use zcompare/3 from library(clpfd) instead of compare/3, you obtain a version that can be used in all directions:
?- bubble(Ls0, Ls, Max).
Ls0 = [Max],
Ls = [] ;
Ls0 = [Max, _G677],
Ls = [_G677],
_G677#=<Max+ -1,
zcompare(<, _G677, Max) ;
Ls0 = [Max, _G949, _G952],
Ls = [_G949, _G952],
_G952#=<Max+ -1,
_G949#=<Max+ -1,
zcompare(<, _G952, Max),
zcompare(<, _G949, Max) ;
etc.
This describes the relation in general terms between integers.
Disclaimer: following the hint by #mat could be more rewarding...
I've played a bit with your code, in my experiment the local stack overflow was thrown with a list length near 2500. Then I've placed some cut:
%% bubble(L, Ls, max):- insert list L and get max member of list by
%% swapping members from the start of L.
bubble([Z], [], Z).
bubble([X,Y|L], [R|Ls], Z):-
( X =< Y -> (R,T)=(X,Y) ; (R,T)=(Y,X) ),
bubble([T|L], Ls, Z).
%% bubble_sort(List, Accumulator, Sorted_List)
bubblesort([X], Ls, [X|Ls]) :- !.
bubblesort(L, Accumulate, Result):-
bubble(L, Ls, Max),
!, bubblesort(Ls, [Max|Accumulate], Result).
and I get
?- time(generate_list(100,10000,L)),time(bubble_sort(L,S)).
% 60,000 inferences, 0.037 CPU in 0.037 seconds (99% CPU, 1618231 Lips)
% 174,710,407 inferences, 85.707 CPU in 86.016 seconds (100% CPU, 2038460 Lips)
L = [98, 19, 80, 24, 16, 59, 70, 39, 22|...],
S = [0, 0, 0, 0, 0, 0, 0, 0, 0|...]
.
so, it's working, but very slowly, showing the quadratic complexity...

CLP in Prolog involving consecutive sums in a list

Example of my CLP problem (this is a small part of a larger problem which uses the clpfd library):
For a list of length 5, a fact el_sum(Pos,N,Sum) specifies that the N consecutive elements starting from position Pos (index from 1) have sum equal to Sum. So if we have
el_sum(1,3,4).
el_sum(2,2,3).
el_sum(4,2,5).
Then [1,2,1,4,1] would work for this example since 1+2+1=4, 2+1=3, 4+1=5.
I'm struggling with how to even start using the el_sum's to find solutions with an input list [X1,X2,X3,X4,X5]. I'm thinking I should use findall but I'm not really getting anywhere.
(My actual problem is much bigger than this so I'm looking for a solution that doesn't just work for three facts and a small list).
Thanks!
You are mixing here the monotonic world of constraints with some non-monotonic quantification. Don't try to mix them too closely. Instead, first transform those facts into, say, a list of terms.
el_sums(Gs) :-
G = el_sum(_,_,_),
findall(G, G, Gs).
And then, only then, start with the constraint part that will now remain monotonic. So:
?- el_sums(Gs), length(L5,5), maplist(l5_(L5), Gs).
l5_(L5, el_sum(P, N, S)) :-
length([_|Pre], P),
length(Cs, N),
phrase((seq(Pre),seq(Cs),seq(_)), L5),
list_sum(Cs,S).
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
Not sure this will help, I don't understand your workflow... from where the list do come ? Anyway
:- [library(clpfd)].
el_sum(Pos,N,Sum) :-
length(L, 5),
L ins 0..100,
el_sum(Pos,N,Sum,L),
label(L), writeln(L).
el_sum(P,N,Sum,L) :-
N #> 0,
M #= N-1,
Q #= P+1,
el_sum(Q,M,Sum1,L),
element(N,L,T),
Sum #= Sum1 + T.
el_sum(_P,0,0,_L).
yields
?- el_sum(1,2,3).
[0,3,0,0,0]
true ;
[0,3,0,0,1]
true ;
...

Resources