Rebol3 Code Examplex
Knapsack problem/Bounded
Solve knapsack with limited item copies.
Rebol [
title: "Rosetta code: Knapsack problem/Bounded"
file: %Knapsack_problem-Bounded.r3
url: https://rosettacode.org/wiki/Knapsack_problem/Bounded
]
solve-knapsack: function/with [
"Solves the bounded knapsack problem via top-down dynamic programming."
limit [integer!] "remaining weight capacity"
pos [integer!] "current item index (0-based)"
][
;; Returns a 3-element block [value weight taken] where `taken` is a
;; block of per-item counts (parallel to `items`) representing the
;; optimal selection, or none if nothing fits.
;; Results are memoised in `cache` keyed by "limit,pos" so each
;; sub-problem is solved at most once.
if any [pos < 0 limit <= 0] [ ;; base case: no items or no capacity
return reduce [0 0 none]
]
key-str: rejoin [limit "," pos]
if cached: cache/:key-str [ return cached ] ;; memoisation hit
item: items/(pos + 1) ;; [name weight value max-qty]
best-value: 0
best-weight: 0
best-count: 0
best-taken: none
taken: 0
while [all [taken * item/2 <= limit taken <= item/4]] [ ;; try 0..max-qty copies
sub: solve-knapsack (limit - (taken * item/2)) (pos - 1) ;; best for remaining items
sub-value: sub/1
sub-weight: sub/2
sub-taken: sub/3
candidate-value: sub-value + (taken * item/3)
if candidate-value > best-value [ ;; keep best combination found so far
best-value: candidate-value
best-weight: sub-weight
best-count: taken
best-taken: sub-taken
]
++ taken
]
if best-count > 0 [
new-taken: make block! num-items ;; copy before mutating to avoid aliasing cached entries
repeat k num-items [append new-taken 0]
if block? best-taken [
repeat k num-items [new-taken/:k: best-taken/:k]
]
new-taken/(pos + 1): best-count ;; record how many of this item we take
best-weight: best-weight + (best-count * item/2)
best-taken: new-taken
]
result: reduce [best-value best-weight best-taken]
cache/(key-str): result ;; store [value weight taken-counts] in memo
result
][
cache: make map! 2000 ;; memoisation table, shared across calls
]
items: [
["map" 9 150 1]
["compass" 13 35 1]
["water" 153 200 2]
["sandwich" 50 60 2]
["glucose" 15 60 2]
["tin" 68 45 3]
["banana" 27 60 3]
["apple" 39 40 3]
["cheese" 23 30 1]
["beer" 52 10 3]
["suntancream" 11 70 1]
["camera" 32 30 1]
["T-shirt" 24 15 2]
["trousers" 48 10 2]
["umbrella" 73 40 1]
["w-trousers" 42 70 1]
["w-overclothes" 43 75 1]
["note-case" 22 80 1]
["sunglasses" 7 20 1]
["towel" 18 12 2]
["socks" 4 50 1]
["book" 30 10 2]
]
item-name: func [item] [item/1]
item-weight: func [item] [item/2]
item-value: func [item] [item/3]
item-limit: func [item] [item/4]
num-items: length? items
result: solve-knapsack 400 (num-items - 1)
total-value: result/1
total-weight: result/2
taken: result/3
print "Taking:"
if block? taken [
repeat i num-items [
count: pick taken i
if count > 0 [
item: pick items i
print [" " count "of" item-limit item " " item-name item]
]
]
]
print ajoin ["Value: " total-value "; weight: " total-weight]