2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -1,9 +1,8 @@
|
|||
See also: [[Knapsack problem/Bounded]], [[Knapsack problem/0-1]]
|
||||
A traveler gets diverted and has to make an unscheduled stop in what turns out to be Shangri La. Opting to leave, he is allowed to take as much as he likes of the following items, so long as it will fit in his knapsack, and he can carry it.
|
||||
|
||||
A traveller gets diverted and has to make an unscheduled stop in what turns out to be Shangri La. Opting to leave, he is allowed to take as much as he likes of the following items, so long as it will fit in his knapsack, and he can carry it.
|
||||
He knows that he can carry no more than 25 'weights' in total; and that the capacity of his knapsack is 0.25 'cubic lengths'.
|
||||
He knows that he can carry no more than 25 'weights' in total; and that the capacity of his knapsack is 0.25 'cubic lengths'.
|
||||
|
||||
Looking just above the bar codes on the items he finds their weights and volumes. He digs out his recent copy of a financial paper and gets the value of each item.
|
||||
Looking just above the bar codes on the items he finds their weights and volumes. He digs out his recent copy of a financial paper and gets the value of each item.
|
||||
<table
|
||||
style="text-align: left; width: 80%;" border="4"
|
||||
cellpadding="2" cellspacing="2"><tr><td
|
||||
|
|
@ -44,14 +43,20 @@ Looking just above the bar codes on the items he finds their weights and volumes
|
|||
style="background-color: rgb(255, 204, 255);" align="left"
|
||||
nowrap="nowrap" valign="middle"><=25</td><td
|
||||
style="background-color: rgb(255, 204, 255);" align="left"
|
||||
nowrap="nowrap" valign="middle"><=0.25 </td></tr></table>
|
||||
nowrap="nowrap" valign="middle"><=0.25 </td></tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
He can only take whole units of any item, but there is much more of any item than he could ever carry
|
||||
|
||||
'''How many of each item does he take to maximise the value of items he is carrying away with him?'''
|
||||
|
||||
Note:
|
||||
# There are four solutions that maximise the value taken. Only one ''need'' be given.
|
||||
;Task:
|
||||
Show how many of each item does he take to maximize the value of items he is carrying away with him.
|
||||
|
||||
|
||||
;Note:
|
||||
* There are four solutions that maximize the value taken. Only one ''need'' be given.
|
||||
|
||||
|
||||
<!-- All solutions
|
||||
|
||||
|
|
@ -63,3 +68,8 @@ Note:
|
|||
|
||||
# (9, 0, 11) also minimizes weight and volume within the limits of calculation
|
||||
-->
|
||||
|
||||
;Related tasks:
|
||||
* [[Knapsack problem/Bounded]]
|
||||
* [[Knapsack problem/0-1]]
|
||||
<br><br>
|
||||
|
|
|
|||
|
|
@ -1,52 +1,58 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct {
|
||||
double val, wgt, vol;
|
||||
const char * name;
|
||||
} items[] = { // value in hundreds, volume in thousandths
|
||||
{30, .3, 25, "panacea"},
|
||||
{18, .2, 15, "ichor"},
|
||||
{25, 2., 2, "gold"},
|
||||
{0,0,0,0}
|
||||
typedef struct {
|
||||
char *name;
|
||||
double value;
|
||||
double weight;
|
||||
double volume;
|
||||
} item_t;
|
||||
|
||||
item_t items[] = {
|
||||
{"panacea", 3000.0, 0.3, 0.025},
|
||||
{"ichor", 1800.0, 0.2, 0.015},
|
||||
{"gold", 2500.0, 2.0, 0.002},
|
||||
};
|
||||
|
||||
/* silly setup for silly task */
|
||||
int best_cnt[16] = {0}, cnt[16] = {0};
|
||||
double best_v = 0;
|
||||
int n = sizeof (items) / sizeof (item_t);
|
||||
int *count;
|
||||
int *best;
|
||||
double best_value;
|
||||
|
||||
void grab_em(int idx, double cap_v, double cap_w, double v)
|
||||
{
|
||||
double val;
|
||||
int t = cap_w / items[idx].wgt;
|
||||
cnt[idx] = cap_v / items[idx].vol;
|
||||
|
||||
if (cnt[idx] > t) cnt[idx] = t;
|
||||
|
||||
while (cnt[idx] >= 0) {
|
||||
val = v + cnt[idx] * items[idx].val;
|
||||
if (!items[idx + 1].name) {
|
||||
if (val > best_v) {
|
||||
best_v = val;
|
||||
memcpy(best_cnt, cnt, sizeof(int) * (1 + idx));
|
||||
}
|
||||
return;
|
||||
}
|
||||
grab_em(idx + 1, cap_v - cnt[idx] * items[idx].vol,
|
||||
cap_w - cnt[idx] * items[idx].wgt, val);
|
||||
cnt[idx]--;
|
||||
}
|
||||
void knapsack (int i, double value, double weight, double volume) {
|
||||
int j, m1, m2, m;
|
||||
if (i == n) {
|
||||
if (value > best_value) {
|
||||
best_value = value;
|
||||
for (j = 0; j < n; j++) {
|
||||
best[j] = count[j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
m1 = weight / items[i].weight;
|
||||
m2 = volume / items[i].volume;
|
||||
m = m1 < m2 ? m1 : m2;
|
||||
for (count[i] = m; count[i] >= 0; count[i]--) {
|
||||
knapsack(
|
||||
i + 1,
|
||||
value + count[i] * items[i].value,
|
||||
weight - count[i] * items[i].weight,
|
||||
volume - count[i] * items[i].volume
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
grab_em(0, 250, 25, 0);
|
||||
printf("value: %g hundreds\n", best_v);
|
||||
for (i = 0; items[i].name; i++)
|
||||
printf("%d %s\n", best_cnt[i], items[i].name);
|
||||
|
||||
return 0;
|
||||
int main () {
|
||||
count = malloc(n * sizeof (int));
|
||||
best = malloc(n * sizeof (int));
|
||||
best_value = 0;
|
||||
knapsack(0, 0.0, 25.0, 0.25);
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
printf("%d %s\n", best[i], items[i].name);
|
||||
}
|
||||
printf("best value: %.0f\n", best_value);
|
||||
free(count); free(best);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ class KnapsackItem {
|
|||
has $.weight;
|
||||
has $.value;
|
||||
has $.name;
|
||||
method new($volume,$weight,$value, $name) {
|
||||
self.bless(*, :$volume, :$weight, :$value, :$name)
|
||||
|
||||
method new($volume,$weight,$value,$name) {
|
||||
self.bless(:$volume, :$weight, :$value, :$name)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
# Define the items to pack
|
||||
$Item = @(
|
||||
[pscustomobject]@{ Name = 'panacea'; Unit = 'vials' ; value = 3000; Weight = 0.3; Volume = 0.025 }
|
||||
[pscustomobject]@{ Name = 'ichor' ; Unit = 'ampules'; value = 1800; Weight = 0.2; Volume = 0.015 }
|
||||
[pscustomobject]@{ Name = 'gold' ; Unit = 'bars' ; value = 2500; Weight = 2.0; Volume = 0.002 }
|
||||
)
|
||||
|
||||
# Define our maximums
|
||||
$MaxWeight = 25
|
||||
$MaxVolume = 0.25
|
||||
|
||||
# Set our default value to beat
|
||||
$OptimalValue = 0
|
||||
|
||||
# Iterate through the possible quantities of item 0, without going over the weight or volume limit
|
||||
ForEach ( $Qty0 in 0..( [math]::Min( [math]::Truncate( $MaxWeight / $Item[0].Weight ), [math]::Truncate( $MaxVolume / $Item[0].Volume ) ) ) )
|
||||
{
|
||||
# Calculate the remaining space
|
||||
$RemainingWeight = $MaxWeight - $Qty0 * $Item[0].Weight
|
||||
$RemainingVolume = $MaxVolume - $Qty0 * $Item[0].Volume
|
||||
|
||||
# Iterate through the possible quantities of item 1, without going over the weight or volume limit
|
||||
ForEach ( $Qty1 in 0..( [math]::Min( [math]::Truncate( $RemainingWeight / $Item[1].Weight ), [math]::Truncate( $RemainingVolume / $Item[1].Volume ) ) ) )
|
||||
{
|
||||
# Calculate the remaining space
|
||||
$RemainingWeight2 = $RemainingWeight - $Qty1 * $Item[1].Weight
|
||||
$RemainingVolume2 = $RemainingVolume - $Qty1 * $Item[1].Volume
|
||||
|
||||
# Calculate the maximum quantity of item 2 for the remaining space, without going over the weight or volume limit
|
||||
$Qty2 = [math]::Min( [math]::Truncate( $RemainingWeight2 / $Item[2].Weight ), [math]::Truncate( $RemainingVolume2 / $Item[2].Volume ) )
|
||||
|
||||
# Calculate the total value of the items packed
|
||||
$TrialValue = $Qty0 * $Item[0].Value +
|
||||
$Qty1 * $Item[1].Value +
|
||||
$Qty2 * $Item[2].Value
|
||||
|
||||
# Describe the trial solution
|
||||
$Solution = "$Qty0 $($Item[0].Unit) of $($Item[0].Name), "
|
||||
$Solution += "$Qty1 $($Item[1].Unit) of $($Item[1].Name), and "
|
||||
$Solution += "$Qty2 $($Item[2].Unit) of $($Item[2].Name) worth a total of $TrialValue."
|
||||
|
||||
# If the trial value is higher than previous most valuable trial...
|
||||
If ( $TrialValue -gt $OptimalValue )
|
||||
{
|
||||
# Set the new number to beat
|
||||
$OptimalValue = $TrialValue
|
||||
|
||||
# Overwrite the previous optimal solution(s) with the trial solution
|
||||
$Solutions = @( $Solution )
|
||||
}
|
||||
|
||||
# Else if the trial value matches the previous most valuable trial...
|
||||
ElseIf ( $TrialValue -eq $OptimalValue )
|
||||
{
|
||||
# Add the trial solution to the list of optimal solutions
|
||||
$Solutions += @( $Solution )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Show the results
|
||||
$Solutions
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*REXX program solves the knapsack/unbounded problem: highest value, weight, and volume.*/
|
||||
/* value weight volume */
|
||||
maxPanacea=0 /* ═══════ ══════ ══════ */
|
||||
maxIchor =0; panacea.$ = 3000 ; panacea.w = 0.3 ; panacea.v = 0.025
|
||||
maxGold =0; ichor.$ = 1800 ; ichor.w = 0.2 ; ichor.v = 0.015
|
||||
max$ =0; gold.$ = 2500 ; gold.w = 2 ; gold.v = 0.002
|
||||
now. =0; sack.$ = 0 ; sack.w = 25 ; sack.v = 0.25
|
||||
|
||||
maxPanacea= min(sack.w / panacea.w, sack.v / panacea.v)
|
||||
maxIchor = min(sack.w / ichor.w, sack.v / ichor.v)
|
||||
maxGold = min(sack.w / gold.w, sack.v / gold.v)
|
||||
|
||||
do p=0 to maxPanacea
|
||||
do i=0 to maxIchor
|
||||
do g=0 to maxGold
|
||||
now.$= g * gold.$ + i * ichor.$ + p * panacea.$
|
||||
now.w= g * gold.w + i * ichor.w + p * panacea.w
|
||||
now.v= g * gold.v + i * ichor.v + p * panacea.v
|
||||
if now.w > sack.w | now.v > sack.v then iterate
|
||||
if now.$ > max$ then do; maxP=p; maxI=i; maxG=g
|
||||
max$=now.$; maxW=now.w; maxV=now.v
|
||||
end
|
||||
end /*g (gold) */
|
||||
end /*i (ichor) */
|
||||
end /*p (panacea)*/
|
||||
|
||||
Ctot = maxP + maxI + maxG; L = length(Ctot) + 1
|
||||
say ' panacea in sack:' right(maxP, L)
|
||||
say ' ichors in sack:' right(maxI, L)
|
||||
say ' gold items in sack:' right(maxG, L)
|
||||
say '════════════════════' copies("═", L)
|
||||
say 'carrying a total of:' right(cTot, L)
|
||||
say left('', 40) "total value: " max$ / 1
|
||||
say left('', 40) "total weight: " maxW / 1
|
||||
say left('', 40) "total volume: " maxV / 1
|
||||
/*stick a fork in it, we're all done. */
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*REXX program solves the knapsack/unbounded problem: highest value, weight, and volume.*/
|
||||
|
||||
maxPanacea=0 /* value weight volume */
|
||||
maxIchor =0 /* ═══════ ═══════ ══════ */
|
||||
maxGold =0; panacea.$ = 3000 ; panacea.w = 0.3 ; panacea.v = 0.025
|
||||
max$ =0; ichor.$ = 1800 ; ichor.w = 0.2 ; ichor.v = 0.015
|
||||
now. =0; gold.$ = 2500 ; gold.w = 2 ; gold.v = 0.002
|
||||
# =0; sack.$ = 0 ; sack.w = 25 ; sack.v = 0.25
|
||||
L =0
|
||||
maxPanacea= min(sack.w / panacea.w, sack.v / panacea.v)
|
||||
maxIchor = min(sack.w / ichor.w, sack.v / ichor.v)
|
||||
maxGold = min(sack.w / gold.w, sack.v / gold.v)
|
||||
|
||||
do p=0 to maxPanacea
|
||||
do i=0 to maxIchor
|
||||
do g=0 to maxGold
|
||||
now.$ = g * gold.$ + i * ichor.$ + p * panacea.$
|
||||
now.w = g * gold.w + i * ichor.w + p * panacea.w
|
||||
now.v = g * gold.v + i * ichor.v + p * panacea.v
|
||||
if now.w > sack.w | now.v > sack.v then iterate i
|
||||
if now.$ > max$ then do; #=0; max$=now.$; end
|
||||
if now.$ = max$ then do; #=#+1; maxP.#=p; maxI.#=i; maxG.#=g
|
||||
max$.#=now.$; maxW.#=now.w; maxV.#=now.v
|
||||
L=max(L, length(p + i + g) )
|
||||
end
|
||||
end /*g (gold) */
|
||||
end /*i (ichor) */
|
||||
end /*p (panacea)*/
|
||||
L=L + 1
|
||||
do j=1 for #; say; say copies('▒', 70) "solution" j
|
||||
say ' panacea in sack:' right(maxP.j, L)
|
||||
say ' ichors in sack:' right(maxI.j, L)
|
||||
say ' gold items in sack:' right(maxG.j, L)
|
||||
say '════════════════════' copies("═", L)
|
||||
say 'carrying a total of:' right(maxP.j + maxI.j + maxG.j, L)
|
||||
say left('', 40) "total value: " max$.j / 1
|
||||
say left('', 40) "total weight: " maxW.j / 1
|
||||
say left('', 40) "total volume: " maxV.j / 1
|
||||
end /*j*/
|
||||
/*stick a fork in it, we're all done. */
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*REXX program solves a knapsack/unbounded problem. */
|
||||
maxPanacea = 0
|
||||
maxIchor = 0
|
||||
maxGold = 0
|
||||
max$ = 0
|
||||
current. = 0
|
||||
/* value weight volume */
|
||||
/* ═══════ ═══════ ══════ */
|
||||
panacea.$ = 3000 ; panacea.w = 0.3 ; panacea.v = 0.025
|
||||
ichor.$ = 1800 ; ichor.w = 0.2 ; ichor.v = 0.015
|
||||
gold.$ = 2500 ; gold.w = 2 ; gold.v = 0.002
|
||||
sack.$ = 0 ; sack.w = 25 ; sack.v = 0.25
|
||||
|
||||
maxPanacea = min(sack.w / panacea.w, sack.v / panacea.v)
|
||||
maxIchor = min(sack.w / ichor.w, sack.v / ichor.v)
|
||||
maxGold = min(sack.w / gold.w, sack.v / gold.v)
|
||||
|
||||
do p=0 to maxpanacea
|
||||
do i=0 to maxichor
|
||||
do g=0 to maxgold
|
||||
current.$ = g*gold.$ + i*ichor.$ + p*panacea.$
|
||||
current.w = g*gold.w + i*ichor.w + p*panacea.w
|
||||
current.v = g*gold.v + i*ichor.v + p*panacea.v
|
||||
if current.w>sack.w | current.v>sack.v then iterate
|
||||
if current.$>max$ then do
|
||||
max$ = current.$
|
||||
totalW = current.w
|
||||
totalV = current.v
|
||||
maxP = p; maxI = i; maxG = g
|
||||
end
|
||||
end /*g (gold) */
|
||||
end /*i (ichor) */
|
||||
end /*p (panacea)*/
|
||||
|
||||
cTot = maxP + maxI + maxG
|
||||
L = length(cTot) + 1
|
||||
say ' panacea in sack:' right(maxP,L)
|
||||
say ' ichors in sack:' right(maxI,L)
|
||||
say ' gold items in sack:' right(maxG,L)
|
||||
say '════════════════════' copies('═',L)
|
||||
say 'carrying a total of:' right(cTot,L)
|
||||
say left('',40) 'total value: ' max$/1
|
||||
say left('',40) 'total weight: ' totalW/1
|
||||
say left('',40) 'total volume: ' totalV/1
|
||||
/*stick a fork in it, we're done.*/
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* create SAS data set */
|
||||
data mydata;
|
||||
input Item $1-19 Value weight Volume;
|
||||
datalines;
|
||||
panacea (vials of) 3000 0.3 0.025
|
||||
ichor (ampules of) 1800 0.2 0.015
|
||||
gold (bars) 2500 2.0 0.002
|
||||
;
|
||||
|
||||
/* call OPTMODEL procedure in SAS/OR */
|
||||
proc optmodel;
|
||||
/* declare sets and parameters, and read input data */
|
||||
set <str> ITEMS;
|
||||
num value {ITEMS};
|
||||
num weight {ITEMS};
|
||||
num volume {ITEMS};
|
||||
read data mydata into ITEMS=[item] value weight volume;
|
||||
|
||||
/* declare variables, objective, and constraints */
|
||||
var NumSelected {ITEMS} >= 0 integer;
|
||||
max TotalValue = sum {i in ITEMS} value[i] * NumSelected[i];
|
||||
con WeightCon:
|
||||
sum {i in ITEMS} weight[i] * NumSelected[i] <= 25;
|
||||
con VolumeCon:
|
||||
sum {i in ITEMS} volume[i] * NumSelected[i] <= 0.25;
|
||||
|
||||
/* call mixed integer linear programming (MILP) solver */
|
||||
solve;
|
||||
|
||||
/* print optimal solution */
|
||||
print TotalValue;
|
||||
print NumSelected;
|
||||
|
||||
/* to get all optimal solutions, call CLP solver instead */
|
||||
solve with CLP / findallsolns;
|
||||
|
||||
/* print all optimal solutions */
|
||||
print TotalValue;
|
||||
for {s in 1.._NSOL_} print {i in ITEMS} NumSelected[i].sol[s];
|
||||
quit;
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import scala.annotation.tailrec
|
||||
|
||||
case class Item(name: String, value: Int, weight: Double, volume: Double)
|
||||
|
||||
val items = List(
|
||||
Item("panacea", 3000, 0.3, 0.025),
|
||||
Item("ichor", 1800, 0.2, 0.015),
|
||||
Item("gold", 2500, 2.0, 0.002))
|
||||
|
||||
val (maxWeight, maxVolume) = (25, 0.25)
|
||||
|
||||
def show(is: List[Item]) =
|
||||
(items.map(_.name) zip items.map(i => is.count(_ == i))).map {
|
||||
case (i, c) => s"$i: $c"
|
||||
}.mkString(", ")
|
||||
|
||||
case class Knapsack(items: List[Item]) {
|
||||
def value = items.foldLeft(0)(_ + _.value)
|
||||
def weight = items.foldLeft(0.0)(_ + _.weight)
|
||||
def volume = items.foldLeft(0.0)(_ + _.volume)
|
||||
def isFull = !((weight <= maxWeight) && (volume <= maxVolume))
|
||||
override def toString =
|
||||
s"[${show(items)} | value: $value, weight: $weight, volume: $volume]"
|
||||
}
|
||||
|
||||
def fill(knapsack: Knapsack): List[Knapsack] =
|
||||
items.map(i => Knapsack(i :: knapsack.items))
|
||||
|
||||
//cause brute force
|
||||
def distinct(list: List[Knapsack]) =
|
||||
list.map(k => Knapsack(k.items.sortBy(_.name))).distinct
|
||||
|
||||
@tailrec
|
||||
def f(notPacked: List[Knapsack], packed: List[Knapsack]): List[Knapsack] =
|
||||
notPacked match {
|
||||
case Nil => packed.sortBy(_.value).takeRight(4)
|
||||
case _ =>
|
||||
val notFull = distinct(notPacked.flatMap(fill)).filterNot(_.isFull)
|
||||
f(notFull, notPacked ::: packed)
|
||||
}
|
||||
|
||||
f(items.map(i => Knapsack(List(i))), Nil).foreach(println)
|
||||
Loading…
Add table
Add a link
Reference in a new issue