I saw this ECLiPSe solution to the problem mentioned in this XKCD comic. I tried to convert this to pure Prolog.
go:-
Total = 1505,
Prices = [215, 275, 335, 355, 420, 580],
length(Prices, N),
length(Amounts, N),
totalCost(Prices, Amounts, 0, Total),
writeln(Total).
totalCost([], [], TotalSoFar, TotalSoFar).
totalCost([P|Prices], [A|Amounts], TotalSoFar, EndTotal):-
between(0, 10, A),
Cost is P*A,
TotalSoFar1 is TotalSoFar + Cost,
totalCost(Prices, Amounts, TotalSoFar1, EndTotal).
I don't think that this is the best / most declarative solution that one can come up with. Does anyone have any suggestions for improvement? Thanks in advance!
Theorem 1 Knapsack is NP-complete. Proof: First of all, Knapsack is NP. The proof is the set S of items that are chosen and the verification process is to compute ∑i∈S si and ∑i∈S vi, which takes polynomial time in the size of input.
The knapsack problem, though NP-Hard, is one of a collection of algorithms that can still be approximated to any specified degree. This means that the problem has a polynomial time approximation scheme. To be exact, the knapsack problem has a fully polynomial time approximation scheme (FPTAS).
This algorithm is polynomial in the values of A and B, which are exponential in their numbers of bits. However, Subset Sum encoded in unary is in P, since then the size of the encoding is linear in B-A. Hence, Subset Sum is only weakly NP-Complete.
0-1 Knapsack cannot be solved by Greedy approach. Greedy approach does not ensure an optimal solution. In many instances, Greedy approach may give an optimal solution.
Since you mention SWI-Prolog why not
?- use_module(library(clpfd)).
and library(lambda)
?- Total = 1505, Prices = [215, 275, 335, 355, 420, 580],
maplist(\P^A^M^(P*A #= M, A #>=0),Prices,Amounts,Ms), sum(Ms, #=, Total).
By stating this, all variables in the list Amounts
are in a finite range. So there is no need to "do the math" for an upper bound (which often goes wrong anyway).
To see concrete solutions, labeling/2 is needed:
?- Total = 1505, Prices = [215, 275, 335, 355, 420, 580],
maplist(\P^A^M^(P*A #= M, A #>=0),Prices,Amounts,Ms), sum(Ms, #=, Total),
labeling([], Amounts).
Total = 1505,
Prices = [215,275,335,355,420,580],
Amounts = [1,0,0,2,0,1],
Ms = [215,0,0,710,0,580] ;
Total = 1505,
Prices = [215,275,335,355,420,580],
Amounts = [7,0,0,0,0,0],
Ms = [1505,0,0,0,0,0].
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With