How to call a function in foreach with local variables as arguments? - prolog

I have a knapsack problem to solve in B-Prolog. I have to write something like:
knapsack(X, Indexes, Quantity, Weights, Values, Capacity)
X - array with number of ith item that is put in the bag
Indexes - [0, ..., NumOfItems-1]
Quantity - array with number of available ith items
Weights - array with weight of each item on ith position (only 1 item)
Value - array with value of each item (only 1 item)
Capacity - limit for bag capacity
The task must be solved using a predicate:
get([X|_], 0, X).
get([_|X], I, Y) :- I>0, I1 is I-1, get(X, I1, Y).
which returns the element on ith index, considering they can start at 0. Then Y has the value of that element.
Also, we must maximize the item values. I tried to make it work not only for fixed number of items.
Since I'm just a beginer for Prolog, I had an idea, but it doesn't wort of course.
knapsack(X, Indexes, Quantity, Weights, Values, Capacity) :-
/*
Find the last index of items,
Calculate NumOfItems as last index+1,
Make an array X with NumOfItems elements,
Define domen for each item (ith element in X) as 0..NumOfThatItem
(meaning I can take zero or more up to the max number of that element that is avaliable)
*/
last(Indexes, Elem),
NumOfItems is Elem+1,
length(X, NumOfItems),
foreach(I in 1..NumOfItems, get(Quantity, I, K), X[I]::0..K),
/*
Set up the constraint that sum of item weights must not be bigger than bag capacity
*/
sum([X[I]*T : I in 1..NumOfItems], get(Weights, I, T)) #=< Capacity,
/*
Maximize the values of items in the bag, and find all possible combinations for it
*/
labeling([maximize( sum([X[I]*V : I in 1..NumOfItems, get(Values, I, V)]))], X),
/*
This is the part of a task, to write out the Profit we made by taking those items,
and the overall weight that we have put in the bag.
*/
Profit is sum([X[I]*V : I in 1..NumOfItems, get(Values, I, V)]),
Weight is sum([X[I]*T : I in 1..NumOfItems, get(Weights, I, T)]),
nl,
write('Profit: '), write(Profit), nl,
write('Weight: '), write(Weight), nl
.
I am using B-Prolog Version 8.1, it can be downloaded on this link(<- click)
You can copy my code and place it in the BProlog folder on the location where you chose to install it.
When you open/start bp aplication:
cl('path_to_my_code.pro').
Exaple I have for this problem is the following:
knapsack(X, [0,1,2,3], [1,1,1,4], [50,10,5,1], [1000,2000,100,1], 63).
And that should give us:
Profit: 3003
Weight: 63
X = [1,1,0,3]
I get the following:
***illegal_local_variables(get([1,1,1,4], _f8, _fc))
I concluded that he doesn't recognize I as a number.
If you have any book or article or whatever related to this please share.
How should this be done? Please help...
Thank you for your time

At this point since you get the error you should test get/3 predicate to see if this working properly. The problem is in the part:
I>0, I1 is I-1, get(X, I1, Y).
Since you call get with I as a variable, I>0 yields instantiation error, instead you can write:
get([X|_], 0, X).
get([_|X], I, Y) :- get(X, I1, Y), I is I1+1, I>0.

Related

How to maximize the goal in prolog?

I am trying to solve the knapsack problem in prolog. Following is my implementation.
% 'ks' is compound term which has 4 argumets
% 1 - List of items to be chosen from.
% 2 - Maximum weight a knapsack can carry.
% 3 - Selected items which sum of weights is less than or equal to knapsack capacity.
% 4 - The gain after choosing the selected item.
% base conditions where input list contains only one items and
% it is either selected or excluded.
ks([item(W1, V1)], W, [item(W1, V1)], V1):- W1 =< W.
ks([item(W1, _)], W, [], 0):- W1 > W.
% An item from the input list is chosen in the knapsack.
% In that case, we recurse with smaller list with reduced weight constraint.
ks(ItemList, MaxWeight, SelectItems, Gain) :-
append(Prefix, [item(W1, V1)|Suffix], ItemList),
append(Prefix, Suffix, RemList),
NewWeight is MaxWeight - W1,
W1 =< MaxWeight,
append([item(W1, V1)], SelectItems1, SelectItems),
ks(RemList, NewWeight, SelectItems1, Gain1),
Gain is V1 + Gain1.
% An item from the input list is not chosen in the knapsack.
% In that case, we recurse with smaller list but with the same weight constraint.
ks(ItemList, MaxWeight, SelectItems, Gain) :-
append([P1|Prefix], [item(W1, V1)|Suffix], ItemList),
append([P1|Prefix], Suffix, RemList),
not(member(item(W1, V1), SelectItems)),
ks(RemList, MaxWeight, SelectItems, Gain).
The input to the program will be list of items as below. in term item(W, V) W is weight of the item while V is value of the item. Goal to maximize the value for the given weight constraint.
ks([item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)], 20, List, Gain).
List = [item(2, 3), item(3, 4), item(4, 5), item(5, 8)],
Gain = 20 ;
While I am able to generate all the combinations of items with above program, I am not able to code to find out the maximum gain only.
Could any one please point me the right direction?
Thanks.
I think that to find reusable abstractions it's an important point of studying programming. If we have a subset_set/2 that yields on backtracking all subsets, ks/4 becomes really simple:
subset_set([], _).
subset_set([H|T], Set) :-
append(_, [H|Rest], Set),
subset_set(T, Rest).
ks(Set, Limit, Choice, Gain) :-
subset_set(Choice, Set),
aggregate((sum(W), sum(G)), member(item(W, G), Choice), (TotWeight, Gain)),
TotWeight =< Limit.
and then
ks_max(Items, Limit, Sel, WMax) :-
aggregate(max(W,I), ks(Items,Limit,I,W), max(WMax,Sel)).
despite its simplicity, subset_set/2 is not really easy to code, and library available alternatives (subset/2, ord_subset/2) don't enumerate, but only check for the relation.
There are at least two things you can do, depending on how you want to approach this.
You could simply collect all solutions and find the maximum. Something along the lines of:
?- Items = [item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)],
findall(Gain-List, ks(Items, 20, List, Gain), Solutions),
sort(Solutions, Sorted),
reverse(Sorted, [MaxGain-MaxList|_]).
% ...
MaxGain = 26,
MaxList = [item(9, 10), item(5, 8), item(4, 5), item(2, 3)].
So you find all solutions, sort them by Gain, and take the last. This is just one way to do it: if you don't mind collecting all solutions, it is up to you how you want to pick out the solution you need from the list. You might also want to find all maximum solutions: see this question and answers for ideas how to do that.
The cleaner approach would be to use constraints. As the comment to your questions points out, it is not very clear what you are actually doing, but the way to go would be to use a library like CLP(FD). With it, you could simply tell labeling/2 to look for the maximum Gain first (once you have expressed your problem in terms of constraints).
greedy Approximation algorithm :
pw((P,W),Res) :- PW is P/W, Res=(PW,P,W).
pws(Ps_Ws,PWs) :- maplist(pw,Ps_Ws,PWs).
sort_desc(List,Desc_list) :-
sort(List,Slist),
reverse(Slist,Desc_list).
ransack_([],_,_,[]).
ransack_([(_,P,W)|PWs],Const,Sum,Res) :-
Sum1 is W+Sum,
Sum1 < Const ->
Res=[(P,W)|Res1],
ransack_(PWs,Const,Sum1,Res1)
;ransack_(PWs,Const,Sum,Res).
% ransack(+[(P,W)|..],+W,,Res)
ransack(L_PWs,W,Res) :-
pws(L_PWs,Aux),
sort_desc(Aux,PWs),
ransack_(PWs,W,0,Res).
Test
item(W, V)-->(V,W)
| ?- ransack([(3,2),(4,3),(5,4),(8,5),(10,9)],20,Res).
Res = [(8,5),(3,2),(4,3),(5,4)] ? ;
no

Trouble displaying constraint values - SICStus clpfd

I am trying to solve a problem for a class that consists of this:
Fill the white cells on the each barrels side with different digits from 1 to 6. Digits cannot
repeat in every horizontal and vertical directions. Each number on the barrels top must
be equal to the sum or product of the four different digits in the barrel. All top numbers
are different and less than 91.
I can achieve the results fine, but I need to display the barrel's results and when I run my base case it shows this:
[_24087,18,60,17,_24343,72,_24471,_24535,14]
[1,2,3,4,5,6,3,4,5,6,1,2,2,5,1,3,6,4,4,6,2,5,3,1,5,1,6,2,4,3,6,3,4,1,2,5]
Result achieved in 0.015 sec.
Resumptions: 6197
Entailments: 1306
Prunings: 3520
Backtracks: 62
Constraints created: 107
The 1st list is the barrels and the 2nd list the Matrix calculated with labeling.
In order to calculate the barrels I use this on a rule:
getlist(Matrix,CounterX,CounterY,InnerSize,Value), % gets the barrel sublist
all_distinct(Value),
sum(Value, #=, SSet), % sum
prod(Value, VSet), % product
Set #= SSet #\/ Set #= VSet, % chooses one
Set #=< MaxValue,
insertinto(Set, List, NewList), % inserts into the barrel list
Since SICStus doesn't have a product calculation rule, I created this one:
prod([H|T], R) :-
prod(T, H, R).
prod([], R, R).
prod([H|T], V, R) :-
NV #= H * V,
prod(T, NV, R).
I don't understand where the problem actually lies.
In my prod rule -> It seems to unify correctly but seems not to when 1 is in the sublist.
How I unify the sum or prod -> Maybe that barrel can be a sum or prod and can't unify with Set correctly.

Prolog - a program that finds a group of values that sum to a particular value

I want to make a program that receives 3 arguments:
list1 of coins for example: [5,2,1]
value - sum we want to get
list of coins that sum to that particular value - this list is a sub-list of list1
(it's allowed to repeat the same element , for ex: to reach 4, we can have the list [2,2])
so the program should do 2 things:
change([5,2,1],4,[2,2]) will return Yes (cause 2+2 =4)
change([5,2],6,Coins) will return Coins = [2,2,2]
this is my attempt:
change(_,0,Res).
change([X|Xs],Sum,Cs):- Sum <X, change(Xs,Sum,Cs).
change([X|Y],Sum,[X|Res]):- Sum>=X, Sum2 is Sum - X, change([X|Y],Sum2,Res).
You need to change Res to [] for the last argument of the first rule. In addition, you should add a cut operator in the same rule to avoid getting the same result multiple times.
change(_, 0, []):-!.
change([X|Y], Sum, [X|Res]):-
Sum >= X, !, % remove the cut operator to get all solutions
Sum2 is Sum - X,
change([X|Y], Sum2, Res).
change([_|Xs],Sum,Cs):-
change(Xs, Sum, Cs).
'guessing' an element from a list can be done with member/2.
Just pay care to termination
change(_,0,[]).
change(Coins,Sum,[C|Cs]) :-
Sum > 0, member(C, Coins), Rest is Sum-C, change(Coins,Rest,Cs).

Prolog inserting multiple elements into list

I want to implement a predicate (vecLine2BitLine) which does the following:
get two lists and a number the first list is the length of blocks (the elements of the blocks are '$') and the second list contains the indexes that these blocks should be placed at meaning:
vecLine2BitLine([1,2,1],[2,5,9],12,BitLine).
BitLine=[' ','$',' ',' ','$','$',' ',' ','$',' ',' ',' '].
explanation:a block of length 1 is at index 2
and a block of length 2 is at index 5 and so on..
insert_at_mul : inserts an element N times (it works perfectly,dupli and my_flatten were implemented previously so i used them)
Ive been trying to activate insert_at_mul N times when N is the length of the list X and Y
in the predicate vecLine2BitLine.
dupli(L1,N,L2) :- dupli(L1,N,L2,N).
dupli([],_,[],_).
dupli([_|Xs],N,Ys,0) :- dupli(Xs,N,Ys,N).
dupli([X|Xs],N,[X|Ys],K) :- K > 0, K1 is K - 1, dupli([X|Xs],N,Ys,K1).
my_flatten(X,[X]) :- \+ is_list(X).
my_flatten([],[]).
my_flatten([X|Xs],Zs) :- my_flatten(X,Y), my_flatten(Xs,Ys), append(Y,Ys,Zs).
insert_at_mul(L,X,K,R,N):-dupli([X],N,XX) , insert_at(L,XX,K,L1) , my_flatten(L1,R).
get_num_spaces(L,N,X):-sum(L,S), X is N-S.
generate_spaces(N,L,X):- insert_at_mul(L,'',1,X,N).
vecLine2BitLineAux([],[],_,_,_).
vecLine2BitLineAux([X|Tail1],[Y|Tail2],N,L,Lnew):- insert_at_mul(L,'*',Y,Lnew,X) ,vecLine2BitLineAux(Tail1,Tail2,N,Lnew,R). // problem here!!!
vecLine2BitLine(X,Y,N,L):- get_num_spaces(X,N,Z) , generate_spaces(Z,[],ZZ) , vecLine2BitLineAux(X,Y,N,ZZ,L).
now the problem is that in the function vecLine2BitLine i cant activate insert_at_mul N times(thats what i tried to do in this code, but failed).
how can I fix vecLine2BitLine for it to work properly as in returning the correct output by actually activating the predicate insert_at_mul N times??
THANKS!
added :
vecLine2BitLine : input parameters : (L1,L2,N,Result)
N: after activating the predicate Result will be N in length.
L1: L1 is a list of numbers each number indicates the length of a block, a block is comprised of a Sequence of '$'.
L2: L2 is a list of numbers the numbers are indices for where the blocks in L1 should be placed.
example:
vecLine2BitLine([3,2],[1,5],9,BitLine).
we can look at the input better as tuples :
vecLine2BitLine[(3,1),(2,5)],9,BitLine).
(3,1) : there is a sequence of '' 3 times at index 1
(2,5) : there is a sequence of '' 2 times at index 5
in our example 9 is the length of BitLine at the end and we have to insert into the
list BitLine 3+2 of the "special chars" '*' but we have 9-(3+2) places left in the list
so we add '' in those places and then we get:
BitLine=['$','$','$','','$','$','','','',''].
This is kind of a nice problem because you can use the arguments as loop counters. The K argument gets you to the proper index. Let's just traverse the list and find a particular index as an example. Notice the base case is that you're at the right element, and the inductive case is prior to the right element.
traverse(1, [X|_], X).
traverse(N, [_|Xs], X) :- N > 0, N0 is N-1, traverse(N0, Xs, X).
We're going to apply that pattern to insert_at/4 to get to the right location in the list. Now let's write a repeat/3 predicate that repeats X N times in a new list L. This time the base case is when we've added all the repetitions we care to, and the inductive case is that we'll add another instance.
repeat(1, X, [X]).
repeat(N, X, [X|Xs]) :- N > 0, N0 is N-1, repeat(N0, X, Xs).
You can see the similarity of structure between these two. Try to combine them into a single predicate. Since this is homework, I'll stop here. You're inches from the goal.

How I can add all the data in a knowledge base in Prolog?

I need help with this exercise of Prolog:
% items
items (cell).
items (labial).
items (control).
items (mirror).
% Weight of each item
weight (cell 2).
weight (labial, 3).
weight (control, 5).
weight (mirror, 10).
capacity (X, Y, Z, V) :-
weight (X C1), weight (Y, C2), weight (Z, C3), sum (C1, C2, C3, R), V> = R.
sum (X, Y, Z, K) :- K is X + Y + Z.
this program does is give me a combination of 3 items or less a given weight, eg capacity (X, Y, Z, 15).
result is, X: cell, Y: Lipstick, Z: mirror, X: control, Y: cell, Z: mirror. successively with all combinations where the sum of the 3 weight no higher input.
At the moment I am limited by the number of income variables manually, capacity (X, Y, Z, N. .......) I want that the combination with respect to number of items that are in the knowledge base, not manually enter the variables. How I can do that?
so would be ideal capacity (weight) and response.
the combination of items where the weight does not exceed
phone, lipstick, mirror.
control labial phone.
mirror, control, labilal .......
Sorry I do not speak English, I'm using google translator.
It looks like the structure that you are looking for is a list and you should look it up ([HD:TL] where TL is a list). The solution I provide below should show how to use lists to the desired effect although my solution allows duplicates and doesn't care about the order of the list. If this is for homework, you should be able to figure out how to fix that on your own once you know a little about lists.
Basically, the first predicate handles making long lists of items, HD is an item with weight X, and it looks for a predicate to define the tail (TL) list; the second handles the end of a list (TL is [] in the previous inductive step) and empty lists (because even empty lists need love too).
items(cell).
items(labial).
items(control).
items(mirror).
weight(cell,2).
weight(labial,3).
weight(control,5).
weight(mirror,10).
capacity(W, [HD|TL]) :- items(HD),weight(HD,X),W>0,capacity(W-X,TL).
capacity(W, []) :- W>=0.

Resources