0-1 Knapsacks
1. Introduction
You have
This is known as the 0-1 Knapsack problem. Naïvely, by trying all the subsets, we can derive a
using Pkg; Pkg.add("Combinatorics"); using Combinatorics;
struct Item value ::Number weight::Number end Weight(ItemList :: Vector{Item}) = sum([item.weight for item in ItemList]) Value(ItemList :: Vector{Item}) = sum([item.value for item in ItemList]) function NaiveKnapsack(ItemList :: Vector{Item}, B) bestvalue = 0 for subset in combinations(ItemList) if Weight(subset) <= B # Valid Subset of Items currvalue = Value(subset) bestvalue = max(currvalue, bestvalue) end end bestvalue end ItemList = [Item(rand(1:100), rand(5:15)) for _ in 1:20] B = 160 NaiveKnapsack(ItemList, B)
This can be sped up by a factor of
2. Meet In The Middle
Algorithm 1 [Horowitz and Sahni 1974] The 0-1 Knapsack Problem can be solved in
Proof Let
function HSKnapsack(ItemList :: Vector{Item}, B) N = length(ItemList) I = ItemList[1 : div(N, 2)] J = ItemList[div(N, 2) + 1 : N] QuerySet = [[Weight(Y), Value(Y)] for Y in combinations(J)] sort!(QuerySet) for i in 2:length(QuerySet) QuerySet[i][2] = max(QuerySet[i][2], QuerySet[i-1][2]) end bestvalue = 0 # Check for Empty X Idx = searchsortedlast(QuerySet, B; by = x -> x[1]) if Idx != 0 bestvalue = QuerySet[Idx][2] end for X in combinations(I) wt = Weight(X) wt > B && continue Idx = searchsortedlast(QuerySet, B - wt; by = x -> x[1]) currvalue = Value(X) if Idx != 0 # Found our set Y currvalue += QuerySet[Idx][2] end bestvalue = max(bestvalue, currvalue) end bestvalue end HSKnapsack(ItemList, B)
The above technique is also known as meet in the middle. Generally, we have a
Problem 2 Can we do better than
Problem 3 Can we do better than
Problem 4 [Andrew Rayskiy] Given
Problem 5 [Dinur, Dunkelman, Keller and Shamir 2014] It is known that a 3 by 3 Rubik's Cube can be solved in 20 moves. Determine an algorithm to find a shortest sequence of moves to solve a scrambled cube.
3. NP-Completeness
A natural question to ask is does there exists a polynomial time algorithm for the 0-1 knapsack problem.
Theorem 6 [Karp 1972] Unless
Problem 7 Consider the Subset Sum problem: Given a set
That’s disappointing. The 0-1 Knapsack problem is unlikely to admit a polynomial time solution. For
4. Dynamic Programming
Algorithm 8 The 0-1 Knapsack problem can be solved in
Proof Define
- it suffices to compute
- for all values of
function f(ItemList :: Vector{Item}, Memoize :: Array{Int, 2}, k, C) (k == 0 || C == 0) && return 0 Memoize[k, C] != -1 && return Memoize[k, C] ans = f(ItemList, Memoize, k-1, C) if C >= ItemList[k].weight ans = max(ans, f(ItemList, Memoize, k-1, C - ItemList[k].weight)+ ItemList[k].value) end Memoize[k, C] = ans end function DPKnapsack(ItemList :: Vector{Item}, B) Memoize = -ones(Int, length(ItemList), B) f(ItemList, Memoize, length(ItemList), B) end DPKnapsack(ItemList, B)
This is not the only special case with an efficient algorithm. For example, if
Problem 9 Let
Problem 10 [Evgeny Zamyatin] Given a multiset
Problem 11 [Gennady Korotkevich] Given a multiset
Problem 12 Determine other special cases of