10.12 Cheatsheet — Knapsack 0/1
Snelle referentie. Klap open wat je nodig hebt.
Het algoritme zelf
De canonieke implementatie (bottom-up DP-tabel)
def knapsack(items, capaciteit):
n = len(items)
tabel = [[0] * (capaciteit + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
waarde, gewicht = items[i - 1]
for w in range(capaciteit + 1):
if gewicht > w:
tabel[i][w] = tabel[i - 1][w]
else:
skip = tabel[i - 1][w]
meenemen = waarde + tabel[i - 1][w - gewicht]
tabel[i][w] = max(skip, meenemen)
return tabel[n][capaciteit]
Welke items? (backtrack uit tabel)
def knapsack_items(items, capaciteit):
n = len(items)
tabel = [[0] * (capaciteit + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
waarde, gewicht = items[i - 1]
for w in range(capaciteit + 1):
if gewicht > w:
tabel[i][w] = tabel[i - 1][w]
else:
tabel[i][w] = max(tabel[i - 1][w], waarde + tabel[i - 1][w - gewicht])
keuze = []
w = capaciteit
for i in range(n, 0, -1):
if tabel[i][w] != tabel[i - 1][w]:
keuze.append(i - 1)
w -= items[i - 1][1]
return keuze
De recurrence
De wiskundige formule (zoals in de docent's slides)
⎧ 0 als i = 0
OPT(i, w) = ⎨ OPT(i-1, w) als wᵢ > w
⎩ max{ OPT(i-1, w), vᵢ + OPT(i-1, w - wᵢ) } anders
Onze Python-functie vult deze drie regels in van linksboven naar rechtsonder in de tabel.
De bouwstenen
1. Items als (waarde, gewicht) tuples
items = [(1, 1), (6, 2), (18, 5), (22, 6), (28, 7)]
waarde, gewicht = items[i - 1]
Python-indexen starten op 0; tabel-rijen starten op 1 (rij 0 = geen
items). Conversie: items[i - 1].
2. Lege tabel
tabel = [[0] * (capaciteit + 1) for _ in range(n + 1)]
(n + 1) × (capaciteit + 1) — vergeet de +1 niet.
3. Eén rij invullen
waarde, gewicht = items[i - 1]
for w in range(capaciteit + 1):
if gewicht > w:
tabel[i][w] = tabel[i - 1][w]
else:
tabel[i][w] = max(tabel[i - 1][w], waarde + tabel[i - 1][w - gewicht])
Twee regels uit de recurrence: te zwaar of max.
4. Alle rijen — buitenste lus
for i in range(1, n + 1):
# vul rij i
range(1, n + 1) — start bij 1 (rij 0 is al klaar), eindigt op n.
5. Backtrack — welke items?
keuze = []
w = capaciteit
for i in range(n, 0, -1):
if tabel[i][w] != tabel[i - 1][w]:
keuze.append(i - 1)
w -= items[i - 1][1]
return keuze
Van n omlaag naar 1. Bij verschil tussen (i, w) en (i-1, w) is
item i meegenomen — index opslaan én w verminderen.
Eigenschappen
Wat lost knapsack 0/1 op?
| Werkt op | Werkt niet op |
|---|---|
| Items met gehele gewichten en waardes | Items in halve hoeveelheden (gebruik fractional knapsack) |
| Capaciteit ≥ 0 | Negatieve capaciteit (zinloos) |
| 0/1 keuze per item | Onbeperkte voorraad per item (gebruik unbounded knapsack) |
Complexiteit
| Versie | Tijd | Ruimte |
|---|---|---|
| Bottom-up DP-tabel (deze track) | O(n × W) | O(n × W) |
| Naïeve recursie (zonder cache) | O(2ⁿ) | O(n) |
| Recursie + memoization | O(n × W) | O(n × W) |
n × W is voor 100 items en W=1000 zo'n 100.000 cellen — fracties
van een seconde op een laptop. Het algoritme schaalt prima voor de
meeste echte problemen.
Zie Big O notatie voor uitleg van die termen.
Top-3 valkuilen
Veelgemaakte fouten
- Off-by-one in tabel-grootte:
(n + 1) × (W + 1), nietn × W. i-loop start bij 0: rij 0 is al klaar. Begin biji = 1.- Backtrack vergeet
wte verminderen bij meegenomen item → verkeerde lijst van items.
Volledig overzicht: stap 11: er gaat iets mis.
Bonus — een recursieve variant
Hetzelfde probleem, top-down
Je kunt knapsack ook recursief schrijven (top-down) in plaats van met een tabel (bottom-up). De recurrence is identiek; het verschil zit in de richting:
def knapsack_recursief(items, capaciteit, n):
if n == 0 or capaciteit == 0:
return 0
waarde, gewicht = items[n - 1]
if gewicht > capaciteit:
return knapsack_recursief(items, capaciteit, n - 1)
skip = knapsack_recursief(items, capaciteit, n - 1)
meenemen = waarde + knapsack_recursief(items, capaciteit - gewicht, n - 1)
return max(skip, meenemen)
Mooi compact, maar zonder cache loopt het tijd op tot O(2ⁿ) — subproblemen worden vele keren herhaald. Voor grote inputs is de tabel-aanpak dus veel sneller.
Verder lezen
- stap 1: het idee — opnieuw door het concept.
- stap 2: vul de tabel in — het algoritme zélf uitgevoerd met de hand.
- stap 8: het complete algoritme — alles samen, met plot van waarde-vs-capaciteit.
- Big O notatie —
O(n × W)versusO(2ⁿ).