2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -1,10 +1,13 @@
|
|||
See also: [[Knapsack problem]] and [[wp:Continuous_knapsack_problem|Wikipedia]].
|
||||
<!-- a thief (or burglar) steals, a robber robs (confronts a person while stealing). Not exactly a perfect definition, but close enough. -- Gerard Schildberger. -->
|
||||
|
||||
A robber burgles a butcher's shop, where he can select from some items. He knows the weights and prices of each items. Because he has a knapsack with 15 kg maximal capacity, he wants to select the items such that he would have his profit maximized. He may cut the items; the item has a reduced price after cutting that is proportional to the original price by the ratio of masses. That means: half of an item has half the price of the original.
|
||||
A thief burgles a butcher's shop, where he can select from some items.
|
||||
|
||||
This is the item list in the butcher's:
|
||||
The thief knows the weights and prices of each items. Because he has a knapsack with 15 kg maximal capacity, he wants to select the items such that he would have his profit maximized. He may cut the items; the item has a reduced price after cutting that is proportional to the original price by the ratio of masses. That means: half of an item has half the price of the original.
|
||||
|
||||
{| style="text-align: left; width: 80%;" border="4" cellpadding="2" cellspacing="2"
|
||||
|
||||
This is the item list in the butcher's shop:
|
||||
|
||||
{| style="text-align: left; width: 50%;" border="4" cellpadding="2" cellspacing="2"
|
||||
|+ Table of potential knapsack items
|
||||
|- style="background-color: rgb(255, 204, 255);"
|
||||
! Item !! Weight (kg) !! Price (Value)
|
||||
|
|
@ -30,4 +33,16 @@ This is the item list in the butcher's:
|
|||
| Knapsack || <=15 kg || ?
|
||||
|}
|
||||
|
||||
'''Which items does the robber carry in his knapsack so that their total weight does not exceed 15 kg, and their total value is maximised?'''
|
||||
|
||||
<br>
|
||||
;Task:
|
||||
Show which items the thief carries in his knapsack so that their total weight does not exceed 15 kg, and their total value is maximized.
|
||||
|
||||
|
||||
;Related task:
|
||||
* [[Knapsack problem]]
|
||||
|
||||
|
||||
;See also:
|
||||
* Wikipedia article: [[wp:Continuous_knapsack_problem|continuous knapsack]].
|
||||
<br><br>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
; Solve Continuous Knapsdack Problem
|
||||
; Solve Continuous Knapsack Problem
|
||||
; Nicolas Modrzyk
|
||||
; January 2015
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
defmodule KnapsackProblem do
|
||||
def price_per_weight( items ), do: (for {name, weight, price} <-items, do: {name, weight, price / weight} )
|
||||
|
||||
def select( max_weight, items ) do
|
||||
{_remains, selected_items} = List.foldr( List.keysort(items, 2), {max_weight, []}, &select_until/2 )
|
||||
selected_items
|
||||
Enum.sort_by( items, fn {_name, weight, price} -> - price / weight end )
|
||||
|> Enum.reduce( {max_weight, []}, &select_until/2 )
|
||||
|> elem(1)
|
||||
|> Enum.reverse
|
||||
end
|
||||
|
||||
def task( max_weight, items ) do
|
||||
def task( items, max_weight ) do
|
||||
IO.puts "The robber takes the following to maximize the value"
|
||||
for {name, weight} <- select( max_weight, price_per_weight(items) ), do: :io.fwrite("~.2f of ~s~n", [weight, name])
|
||||
Enum.each( select( max_weight, items ), fn {name, weight} ->
|
||||
:io.fwrite("~.2f of ~s~n", [weight, name])
|
||||
end )
|
||||
end
|
||||
|
||||
defp select_until( {name, weight, _price}, {remains, acc} ) when remains > 0 do
|
||||
|
|
@ -31,4 +33,4 @@ items = [ {"beef", 3.8, 36},
|
|||
{"salami", 3.0, 95},
|
||||
{"sausage", 5.9, 98} ]
|
||||
|
||||
KnapsackProblem.task( 15, items )
|
||||
KnapsackProblem.task( items, 15 )
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
defmodule KnapsackProblem do
|
||||
def continuous(items, max_weight) do
|
||||
Enum.sort_by(items, fn {_item, {weight, price}} -> -price / weight end)
|
||||
|> Enum.reduce_while({max_weight,0}, fn {item, {weight, price}}, {rest, value} ->
|
||||
if rest > weight do
|
||||
IO.puts "Take all #{item}"
|
||||
{:cont, {rest - weight, value + price}}
|
||||
else
|
||||
:io.format "Take ~.3fkg of ~s~n~n", [rest, item]
|
||||
:io.format "Total value of swag is ~.2f~n", [value + rest*price/weight]
|
||||
{:halt, :ok}
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
{weight, value} ->
|
||||
:io.format "Total: weight ~.3fkg, value ~p~n", [max_weight-weight, value]
|
||||
x -> x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
items = [ beef: {3.8, 36},
|
||||
pork: {5.4, 43},
|
||||
ham: {3.6, 90},
|
||||
greaves: {2.4, 45},
|
||||
flitch: {4.0, 30},
|
||||
brawn: {2.5, 56},
|
||||
welt: {3.7, 67},
|
||||
salami: {3.0, 95},
|
||||
sausage: {5.9, 98} ]
|
||||
|
||||
KnapsackProblem.continuous( items, 15 )
|
||||
|
|
@ -4,8 +4,8 @@ class KnapsackItem {
|
|||
has $.price is rw;
|
||||
has $.ppw;
|
||||
|
||||
method new (Str $n, $w, $p) {
|
||||
KnapsackItem.bless(*, :name($n), :weight($w), :price($p), :ppw($w/$p))
|
||||
method new (Str $n, Rat $w, Int $p) {
|
||||
self.bless(:name($n), :weight($w), :price($p), :ppw($w/$p))
|
||||
}
|
||||
|
||||
method cut-maybe ($max-weight) {
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
/*REXX program solves the (continuous) burglar's knapsack problem. */
|
||||
@.= /*═══════ name weight value ══════*/
|
||||
@.1 = 'flitch 4 30 '
|
||||
@.2 = 'beef 3.8 36 '
|
||||
@.3 = 'pork 5.4 43 '
|
||||
@.4 = 'greaves 2.4 45 '
|
||||
@.5 = 'brawn 2.5 56 '
|
||||
@.6 = 'welt 3.7 67 '
|
||||
@.7 = 'ham 3.6 90 '
|
||||
@.8 = 'salami 3 95 '
|
||||
@.9 = 'sausage 5.9 98 '
|
||||
parse arg maxW d . /*get possible arguments from the C.L. */
|
||||
if maxW=='' | maxW==',' then maxW=15 /*the burglar's knapsack maximum weight*/
|
||||
if d=='' | d==',' then d= 3 /*number of decimal digits in FORMAT. */
|
||||
wL=d+length('weight'); nL=d+length('total weight'); vL=d+length('value')
|
||||
/*REXX pgm solves the continuous burglar's knapsack problem; items with weight and value*/
|
||||
@.= /*═══════ name weight value ══════*/
|
||||
@.1 = 'flitch 4 30 '
|
||||
@.2 = 'beef 3.8 36 '
|
||||
@.3 = 'pork 5.4 43 '
|
||||
@.4 = 'greaves 2.4 45 '
|
||||
@.5 = 'brawn 2.5 56 '
|
||||
@.6 = 'welt 3.7 67 '
|
||||
@.7 = 'ham 3.6 90 '
|
||||
@.8 = 'salami 3 95 '
|
||||
@.9 = 'sausage 5.9 98 '
|
||||
parse arg maxW d . /*get possible arguments from the C.L. */
|
||||
if maxW=='' | maxW=="," then maxW=15 /*the burglar's knapsack maximum weight*/
|
||||
if d=='' | d=="," then d= 3 /*number of decimal digits in FORMAT. */
|
||||
wL=d+length('weight'); nL=d+length("total weight"); vL=d+length('value')
|
||||
totW=0; totV=0
|
||||
do #=1 while @.#\==''; parse var @.# n.# w.# v.# .
|
||||
end /*#*/ /* [↑] assign item to separate lists. */
|
||||
#=#-1 /*#: is the number of items in @ list.*/
|
||||
call show 'unsorted item list' /*display the header and the @ list.*/
|
||||
call sortD /*invoke sort (which sorts descending).*/
|
||||
do #=1 while @.#\==''; parse var @.# n.# w.# v.# .
|
||||
end /*#*/ /* [↑] assign item to separate lists. */
|
||||
#=#-1 /*#: is the number of items in @ list.*/
|
||||
call show 'unsorted item list' /*display the header and the @ list.*/
|
||||
call sortD /*invoke sort (which sorts descending).*/
|
||||
call hdr "burglar's knapsack contents"
|
||||
do j=1 for # while totW<maxW; f=1 /*process items. */
|
||||
if totW+w.j>=maxW then f=(maxW-totW)/w.j /*calculate fract.*/
|
||||
totW=totW+w.j*f; totV=totV+v.j*f /*add ───► totals.*/
|
||||
call syf left(word('{all}',1+(f\==1)),5) n.j, w.j*f, v.j*f
|
||||
end /*j*/ /* [↑] show item.*/
|
||||
call sep; say; t='t' /* [↓] $ suppresses trailing zeroes.*/
|
||||
call sy left('total weight',nL,'─'), $(format(totW,,d))
|
||||
call sy left('total value',nL,'─'), , $(format(totV,,d))
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*────────────────────────────────────────────────────────────────────────────*/
|
||||
sortD: do s=2 to #; a=n.s; !=w.s; u=v.s /* [↓] this is a descending sort.*/
|
||||
do k=s-1 by -1 to 1 while v.k/w.k<u/!;?=k+1;n.?=n.k;w.?=w.k;v.?=v.k;end
|
||||
?=k+1; n.?=a; w.?=!; v.?=u
|
||||
end /*s*/
|
||||
return /* ↑↑↑ sort algorithm is OK for small arrays.*/
|
||||
/*──────────────────────────────────one─liner subroutines─────────────────────*/
|
||||
hdr: say; say; say center(arg(1),50,'─'); say; call title; call sep; return
|
||||
sep: call sy copies('═',nL), copies("═",wL), copies('═',vL); return
|
||||
show: call hdr arg(1); do j=1 for #; call syf n.j,w.j,v.j; end; return
|
||||
sy: say left('',9) left(arg(1),nL) right(arg(2),wL) right(arg(3),vL); return
|
||||
syf: call sy arg(1), $(format(arg(2),,d)), $(format(arg(3),,d)); return
|
||||
title: call sy center('item',nL), center("weight",wL), center('value',vL);return
|
||||
$: arg x; if pos(.,x)>1 then x=left(strip(strip(x,'T',0),,.),length(x));return x
|
||||
do j=1 for # while totW<maxW; f=1 /*process the items. */
|
||||
if totW+w.j>=maxW then f=(maxW-totW)/w.j /*calculate fraction. */
|
||||
totW=totW+w.j*f; totV=totV+v.j*f /*add it ───► totals. */
|
||||
call syf left(word('{all}',1+(f\==1)),5) n.j, w.j*f, v.j*f
|
||||
end /*j*/ /* [↑] display item. */
|
||||
call sep; say ' /* [↓] $ suppresses trailing zeroes.*/
|
||||
call sy left('total weight', nL, "─"), $(format(totW,,d))
|
||||
call sy left('total value', nL, "─"), , $(format(totV,,d))
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
sortD: do s=2 to #; a=n.s; !=w.s; u=v.s /* [↓] this is a descending sort. */
|
||||
do k=s-1 by -1 to 1 while v.k/w.k<u/!; ?=k+1; n.?=n.k; w.?=w.k;v.?=v.k;end
|
||||
?=k+1; n.?=a; w.?=!; v.?=u
|
||||
end /*s*/
|
||||
return /* ↑↑↑ sort algorithm is OK for small arrays*/
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
hdr: say; say; say center(arg(1),50,'─'); say; call title; call sep; return
|
||||
sep: call sy copies('═', nL), copies("═", wL), copies('═', vL); return
|
||||
show: call hdr arg(1); do j=1 for #; call syf n.j, w.j, v.j; end; return
|
||||
sy: say left('',9) left(arg(1),nL) right(arg(2),wL) right(arg(3),vL); return
|
||||
syf: call sy arg(1), $(format(arg(2), , d)), $(format(arg(3), , d)); return
|
||||
title: call sy center('item',nL), center("weight", wL), center('value', vL); return
|
||||
$: parse arg x;if pos(.,x)>1 then x=left(strip(strip(x,'T',0),,.),length(x)); return x
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/* create SAS data set */
|
||||
data mydata;
|
||||
input item $ weight value;
|
||||
datalines;
|
||||
beef 3.8 36
|
||||
pork 5.4 43
|
||||
ham 3.6 90
|
||||
greaves 2.4 45
|
||||
flitch 4.0 30
|
||||
brawn 2.5 56
|
||||
welt 3.7 67
|
||||
salami 3.0 95
|
||||
sausage 5.9 98
|
||||
;
|
||||
|
||||
/* call OPTMODEL procedure in SAS/OR */
|
||||
proc optmodel;
|
||||
/* declare sets and parameters, and read input data */
|
||||
set <str> ITEMS;
|
||||
num weight {ITEMS};
|
||||
num value {ITEMS};
|
||||
read data mydata into ITEMS=[item] weight value;
|
||||
|
||||
/* declare variables, objective, and constraints */
|
||||
var WeightSelected {i in ITEMS} >= 0 <= weight[i];
|
||||
max TotalValue = sum {i in ITEMS} (value[i]/weight[i]) * WeightSelected[i];
|
||||
con WeightCon:
|
||||
sum {i in ITEMS} WeightSelected[i] <= 15;
|
||||
|
||||
/* call linear programming (LP) solver */
|
||||
solve;
|
||||
|
||||
/* print optimal solution */
|
||||
print TotalValue;
|
||||
print {i in ITEMS: WeightSelected[i].sol > 1e-3} WeightSelected;
|
||||
quit;
|
||||
Loading…
Add table
Add a link
Reference in a new issue