prolog:search in binary search tree - prolog

i need to covert list to binary search tree ,then search about range of ages in this tree and return the a list contain these values, and also return number of checks in order to build the output list .
i spend two days trying do this but it always return false
here my last code that i reach it with help of mbratch:
my_list( [[30,'john'], [58,'alex'], [14,'randy'], [65,'shawn'], [67,'jack']] ).
construct(L,T) :- construct(L,T,nil).
construct([],T,T).
construct([N|Ns],T,T0) :- add(N,T0,T1), construct(Ns,T,T1).
add(X, nil, node(X, nil, nil)).
add(X, node(Root, L, R),node(Root, L1, R)) :- X #< Root, add(X, L, L1).
add(X, node(Root, L, R),node(Root, L, R1)) :- X #> Root, add(X, R, R1).
findInRange(R1, R2, T, S, N) :- find(R1, R2, T, S, N),!.
find(_R1,_R2, nil, [], 0).
find(R1, R2, node([Age,Name],L,R), S, N) :-
R1 =< Age,R2 >= Age, % is the age OK (in range), if it is check left and
find(R1, R2, L, LL, NL),
find(R1,R2,R,LR,NR),
append([[Age,Name]],LL,X),
append(X,LR,S),
N is NL+NR+1.
find(R1, R2, node([Age,Name],L,R), [], 0) :-
Age > R2;Age<R1. % if the age is greater than R2, return []
find(R1, R2, node([Age,Name],L,R), LL, N) :-
R1 < Age, % if the age is bigger than R1 search the left tree return LL
find(R1,R2,L,LL,NL),
N is NL+1.
find(R1, R2, node([Age,Name],L,R), LR, N) :-
R2 > Age, % if the age smaller than R1 search the right tree return LR
find(R1,R2,R,LR,NR),
N is NR+1.
and here is my query :
my_list(Z), construct(Z, T), findInRange(11, 15, T, S, N).
it should retufn [[14,'randy']] and number of checks.
Why does it return empty list and N=0 ?

I think this will do the trick. I ran the query you posted and got S = [[30, john], [14, randy]].
my_list( [[30,'john'], [58,'alex'], [14,'randy'], [65,'shawn'], [67,'jack']] ).
construct(L,T) :- construct(L,T,nil).
construct([],T,T).
construct([N|Ns],T,T0) :- add(N,T0,T1),construct(Ns,T,T1).
add(X, nil, node(X, nil, nil)).
add(X, node(Root, L, R),node(Root, L1, R)) :- X #< Root, add(X, L, L1).
add(X, node(Root, L, R),node(Root, L, R1)) :- X #> Root, add(X, R, R1).
findInRange(R1, R2, T, S, N) :- find(R1, R2, T, S, N),!.
find(_R1,_R2, nil, [], 0).
find(R1, R2, node([Age,Name],L,R), S, N) :-
R1 =< Age,R2 >= Age, % is the age OK (in range), if it is check left and right side
find(R1, R2, L, LL, NL),
find(R1,R2,R,LR,NR),
append([[Age,Name]| LL],LR,S),
N is NL+NR+1.
find(R1, R2, node([Age,Name],L,R), LL, N) :-
Age > R2, % if the age is bigger than R2 search the left tree return LL
find(R1,R2,L,LL,NL),
N is NL+1.
find(R1, R2, node([Age,Name],L,R), LR, N) :-
R1 > Age, % if the age smaller than R1 search the right tree return LR
find(R1,R2,R,LR,NR),
N is NR+1.

Related

Prolog - finding a common ancestor in a binary tree

Let's say you have a binary search tree:
t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil)))
which is:
73
/ \
31 101
/ / \
5 83 200
/
97
I need to write a predicate subtree(X1,X2,T) that would take 2 values from the tree (X1 and X2) and find the smallest common parent for them, and store its subtree in T.
So for the example above, if I query : subtree(83,200,X).
I should be getting back:
t(101,t(83,nil,t(97,nil,nil)),t(200,nil,nil))
which is:
101
/ \
83 200
/
97
Since 101 is the smallest common value to both of my numbers, I get that subtree back. How could I do that?
Thanks!
Here is my code for this problem. Just call
tree(X),common_ancestor(83,200,X,Y)
You will get your answer in Y.
tree3(X) :- X = t (73, t (31, t(5,nil,nil), nil), t (101, t (83, nil, t(97,nil,nil)), t(200,nil,nil))).
% Chech membership in tree:
in(X, t(X, _, _)).
in(X, t(_, L, _)):-
in(X, L).
in(X, t(_, _, R)):-
in(X, R).
% Getting subtree given the value of root
get_subtree(X, t(X, L, R),Res) :- Res = t(X,L,R).
get_subtree(X, t(_, L, _),Res):-
get_subtree(X, L,Res).
get_subtree(X, t(_, _, R),Res):-
get_subtree(X, R,Res).
% least_common_ancestor(N1, N2, Tree, Res) assignes the value of the least common ancestor to Res.
% Note that it's assumed all nodes have different values.
% Base cases: when one value is the parent of the other, then the parent is the LCA:
least_common_ancestor(N1, N2, t(N1, L, R), N1):-
(in(N2, L) ; in(N2, R)), !.
least_common_ancestor(N1, N2, t(N2, L, R), N2):-
(in(N1, L) ; in(N1, R)), !.
% If one is in the left (right) subtree and the other is in the right (left) subtree then the current root is the LCA
least_common_ancestor(N1, N2, t(X, L, R), Res):-
((in(N1, L), in(N2, R)) ; (in(N1, R), in(N2, L))), !,
Res = X.
% Otherwise, recurse to both subtrees:
least_common_ancestor(N1, N2, t(_, L, _), Res):-
least_common_ancestor(N1, N2, L, Res), !.
least_common_ancestor(N1, N2, t(_, _, R), Res):-
least_common_ancestor(N1, N2, R, Res).
% The main function
commonGP(Ka,Kb,T,ST) :-
least_common_ancestor(Ka,Kb,T,Res), get_subtree(Res,T,ST).

Prolog | find the path from root to leaf with the maximal sum of node value

I need to create a prolog relation that receives a tree, sums the values in each nodes and finds the path with the maximal sum.
I've tried this method of a max sub tree:
max_sub_tree(Tree,T,N):-
sol_tree_noroot(Tree,T1,N1),
sol_tree_withroot(Tree,T2,N2),!,
max_set(T1,N1,T2,N2,T,N).
max_set(T1, N1, T2, N2, T, N) :-
(N1>N2,T=T1,N=N1;
N2>N1,T=T2,N=N2;
N2=:=N1,N=N1,(T=T1;T=T2)).
sol_tree_noroot(nil,nil,0).
sol_tree_noroot(t(L,_,R),T,N):-
max_sub_tree(L,T1,N1),max_sub_tree(R,T2,N2),!,
max_set(T1, N1, T2, N2, T, N).
sol_tree_withroot(nil,nil,0).
sol_tree_withroot(t(L,X,R),t(L1,X,R1),N3):-
sol_tree_withroot(L,T1,N1),sol_tree_withroot(R,T2,N2),
max_set2(T1,N1,T2,N2,L1,R1,N),
N3 is N+X.
max_set2(T1,N1,T2,N2,L,R,N):-
(N1>0,N2>0,N is N1+N2,L=T1,R=T2;
N1>=0,N2<0,N is N1 ,R=nil,L=T1;
N1<0,N2>=0,N is N2 ,L=nil,R=T2;
N1<0,N2<0,N1<N2,N is N2 ,L=nil,R=T2;
N1<0,N2<0,N1>N2,N is N1 ,L=T1,R=nil;
N1>0,N2=0,N is N1,(L=T1,R=nil;L=T1,R=T2);
N1=0,N2>0,N is N2,(R=T2,L=nil;L=T1,R=T2);
N1=0,N2=0,N is N1,(L=T1,R=nil;R=T2,L=T1;L=T1,R=T2)).
When I use the query
max_sub_tree(t(t(t(nil,2,nil),1,t(t(nil,40,nil),-30,nil)),-100,t(nil,50,t(nil,60,nil))) ,T,N).
I get
N = 110,
T = t(nil, 50, t(nil, 60, nil))
But I want the output to look like this:
N = 10,
T =.. [t, -100, 50, 60]
What Am I missing? how do I include the root? do i need to start over?
Subtree Sums
This looks complicated, might I suggest we start from how to generate the sums of the subtrees that terminate in leaf nodes:
tree_sum(t(nil, N, nil), N). % leaf
tree_sum(t(T, N, nil), X) :- % only left branch
tree_sum(T, M), X is N + M.
tree_sum(t(nil, N, T), X) :- % only right branch
tree_sum(T, M), X is N + M.
tree_sum(t(T1, N, T2), X) :- % branches
( tree_sum(T1, M), X is N + M
; tree_sum(T2, M), X is N + M
).
That disjunction is where we need to focus to find the maximum tree sum, let's add that into our code next. There's no change to the first three rules
max_tree_sum(t(nil, N, nil), N). % leaf
max_tree_sum(t(T, N, nil), X) :- % only left branch
max_tree_sum(T, M), X is N + M.
max_tree_sum(t(nil, N, T), X) :- % only right branch
max_tree_sum(T, M), X is N + M.
max_tree_sum(t(T1, N, T2), X) :-
max_tree_sum(T1, M1), X1 is N + M1,
max_tree_sum(T2, M2), X2 is N + M12,
X is max(X1, X2).
A solution
Ok, so our code is finding the maximum solution, now we need it to track the path, building the list. We add in the final argument for this and an extra sub-predicate to do the comparison of branches for us:
max_tree_sum(t(nil, N, nil), N, [N]). % leaf
max_tree_sum(t(T, N, nil), X, [N|MT]) :- % left branch only
max_tree_sum(T, M, MT), X is N + M.
max_tree_sum(t(nil, N, T), X, [N|MT]) :- % right branch only
max_tree_sum(T, M, MT), X is N + M.
max_tree_sum(t(T1, N, T2), X, [N|T]) :- % branches
max_tree_sum(T1, M1, MT1),
max_tree_sum(T2, M2, MT2),
max_subtree(M1, M2, MT1, MT2, M, T), X is M + N.
max_subtree(N1, N2, T1, _, N1, T1) :-
N1 >= N2.
max_subtree(N1, N2, _, T2, N2, T2) :-
N1 =< N2.
As Requested with T =.. [t|Nodes]
Now if you want the list converted to a predicate, put an extra predicate call to this:
max_subtree_sum(Tree, Sum, Pred) :-
max_tree_sum(Tree, Sum, Path),
Pred =.. [t|L].
?- max_subtree_sum(ExampleTree, 10, t(-100, 50, 60)).
But now t(-100, 50, 60) is not a tree.

get sublist of all elements that create specific word prolog

i want to get a sublist of all elements that assemble specific word for example
for the call
assemble([hello,'',world,hi,bye,good,well], 'hello world', A).
program should print
A=[hello,'',world]
for the call
assemble([abc,123,ab,c,123],'abc123', A).
program should print
A=[abc,123];
A=[ab,c,123];
thanks for your help.
matchwords(W1, W2, Results) :-
setof(R, matchw(W1, W2, R), RSet), % Collect all the matching substrings
% and their lengths
reverse(RSet, Set), % Order by longest first
highest(Set, Results). % keep only the highest ones
matchw(W1, W2, N-Result) :-
atom_chars(W1, A1),
atom_chars(W2, A2),
matchl(A1, A2, R),
length(R, N),
atom_chars(Result, R).
matchl([H|T1], [H|T2], [H|T]) :-
matchl(T1, T2, T).
matchl([H1|T1], [H2|T2], R) :-
H1 \= H2,
( matchl(T1, [H2|T2], R) ; matchl([H1|T1], T2, R) ).
matchl([], _, []).
matchl([_|_], [], []).
highest([_-W], [W]).
highest([N1-W1,N2-_|_], [W1]) :-
N1 > N2.
highest([N1-W1,N2-W2|T], [W1|WT]) :-
N1 = N2,
highest([N2-W2|T], WT).

Count number of check to build the list

I have an error when i count number of check in order to build the list.
Here is my code:
find(_R1, _R2, [], [],0).
find(R1, R2, [[Mark, CName] | T], L,N) :-
( (R1 =< Mark, Mark =< R2),N is N+1
-> L = [CName | L1]
; L = L1
),
find(R1, R2, T, L1,N).
I get the following error:
ERROR: is/2: Arguments are not sufficiently instantiated.
N is N+1 is always false in Prolog , Prolog is not C, you MUST write N1 is N+1, because when N is unified with a value, it can't be changed, so N is never equal to N+1.
Your code must be written like that, the recursion must be called before the calculus of N.
find(_R1, _R2, [], [],0).
find(R1, R2, [[Mark, CName] | T], L,N):-
find(R1, R2, T, L1,N1),
( (R1 =< Mark, Mark =< R2)
-> L = [CName | L1],N is N1+1
; L = L1, N = N1).
[EDIT] In your post, N has no value when you call N is N+1, because N gets a value after the return of the call of find(R1, R2, T, L1,N).

inorder traversal modified for range query in BST prolog

This is the pretty known code for an inorder traversal in a generic binary tree in PROLOG:
inorder(t(K,L,R), List) :-
inorder(L,LL), inorder(R, LR),
append(LL, [K|LR], List).
inorder(nil, []).
if the input binary tree is a binary search tree (BST), what about If I want to modify such code in such way I will not visit just all the nodes, but only the minimum needed to find those ones whose key falls in a given range, having them as output result?
I am trying something like:
inorder(R1, R2, t(K,L,R), List) :-
(K >= R1 -> inorder(R1, R2, L, LL); true),
(K =< R2 -> inorder(R1, R2, R, LR); true),
append(LL, [K|LR], List).
inorder(_, _, _, _).
I figured out the correct version:
inorder(R1,R2, t(K,L,R), List) :-
( R2 < K
-> inorder(R1,R2, L, LL),
append(LL,[],List);
K < R1
-> inorder(R1,R2, R, LR),
append(LR,[],List);
inorder(R1,R2, L, LL),
inorder(R1,R2, R, LR),
append(LL, [K|LR], List)
).
you are 'near to target', and you will benefit of studying what your code does. Here is my solution (only minimally tested)
inorder(R1,R2, t(K,L,R), List) :-
( R2 < K
-> inorder(R1,R2, L, List)
; K < R1
-> inorder(R1,R2, R, List)
; inorder(R1,R2, L, LL),
inorder(R1,R2, R, LR),
append(LL, [K|LR], List)
).
inorder(_R1,_R2, -, []).
of course, I like best a DCG solution
inorder(R1,R2, Tree, Yield) :-
phrase(inorder(R1, R2, Tree), Yield, []).
inorder(R1,R2, t(K,L,R)) -->
{R2 < K}
-> inorder(R1,R2,L)
; {K < R1}
-> inorder(R1,R2,R)
; inorder(R1,R2,L),
[K],
inorder(R1,R2,R).
inorder(_R1,_R2, -) --> [].
note: no more append/3 needed. It's cleaner and more efficient.
edit better naming:
inorder(R1,R2, Tree, Yield) :-
phrase(rangequery(R1, R2, Tree), Yield, []).
rangequery(R1,R2, t(K,L,R)) -->
{R2 < K}
-> rangequery(R1,R2,L)
; {K < R1}
-> rangequery(R1,R2,R)
; rangequery(R1,R2,L),
[K],
rangequery(R1,R2,R).
rangequery(_R1,_R2, -) --> [].
and sample run
?- inorder(1,2,t(3,t(1,-,-),-),L).
L = [1] ;
false.
?- inorder(1,4,t(3,t(1,-,t(2,-,-)),-),L).
L = [1, 2, 3] ;
false.

Resources