A question recently posted to
sci.op-research offered me a welcome respite from some tedious work that I’d rather not think about and you’d rather not hear about. We know that the greedy heuristic solves the continuous knapsack problem
maximizec′xs.t.a′x≤bx≤ux∈Rn+(1)
to optimality. (The proof, using duality theory, is quite easy.) Suppose that we add what I’ll call a count constraint, yielding
maximizec′xs.t.a′x≤be′x=~bx≤ux∈Rn+(2)
where
e=(1,…,1)
. Can it be solved by something other than the simplex method, such as a variant of the greedy heuristic?
The answer is yes, although I’m not at all sure that what I came up with is any easier to program or more efficient than the simplex method. Personally, I would link to a library with a linear programming solver and use simplex, but it was amusing to find an alternative even if the alternative may not be an improvement over simplex.
The method I’ll present relies on duality, specifically a well known result that if a feasible solution to a linear program and a feasible solution to its dual satisfy the complementary slackness condition, then both are optimal in their respective problems. I will denote the dual variables for the knapsack and count constraints λ
and μ
respectively. Note that λ≥0
but μ
is unrestricted in sign. Essentially the same method stated below would work with an inequality count constraint (e′x≤~b
), and would in fact be slightly easier, since we would know a priori the sign of μ
(nonnegative). The poster of the original question specified an equality count constraint, so that’s what I’ll use. There are also dual variables (ρ≥0
) for the upper bounds. The dual problem is
minimizebλ+~bμ+u′ρs.t.λa+μe+ρ≥cλ,ρ≥0.(3)
This being a blog post and not a dissertation, I’ll assume that (2) is feasible, that all parameters are strictly positive, and that the optimal solution is unique and not degenerate. Uniqueness and degeneracy will not cause invalidate the algorithm, but they would complicate the presentation. In an optimal basic feasible solution to (2), there will be either one or two basic variables — one if the knapsack constraint is nonbinding, two if it is binding — with every other variable nonbasic at either its lower or upper bound. Suppose that (λ,μ,ρ)
is an optimal solution to the dual of (2). The reduced cost of any variable xi
is ri=ci−λai−μ
. If the knapsack constraint is nonbinding, then λ=0
and the optimal solution is
xi=⎧⎪⎨⎪⎩uiri>0~b−∑rj>0ujri=00ri<0.(4)
If the knapsack constraint is binding, there will be two items (j
, k
) whose variables are basic, with rj=rk=0
. (By assuming away degeneracy, I’ve assumed away the possibility of the slack variable in the knapsack constraint being basic with value 0). Set
xi={uiri>00ri<0(5)
and let b′=b−∑i∉{j,k}aixi
and ~b′=~b−∑i∉{j,k}xi
. The two basic variables are given by
xj=b′−ak~b′aj−akxk=b′−aj~b′ak−aj.(6)
The algorithm will proceed in two stages, first looking for a solution with the knapsack nonbinding (one basic x
variable) and then looking for a solution with the knapsack binding (two basic x
variables). Note that the first time we find feasible primal and dual solutions obeying complementary slackness, both must be optimal, so we are done. Also note that, given any μ
and any λ≥0
, we can complete it to obtain a feasible solution to (3) by setting ρi=(ci−λai−μ)+
. So we will always be dealing with a feasible dual solution, and the algorithm will construct primal solutions that satisfy complementary slackness. The stopping criterion therefore reduces to the constructed primal solution being feasible.
For the first phase, we sort the variables so that c1≥⋯≥cn
. Since λ=0
and there is a single basic variable (xh
), whose reduced cost must be zero, obviously μ=ch
. That means the reduced cost ri=ci−λai−μ=ci−ch
of xi
is nonnegative for i<h
and nonpositive for i>h
. If the solution given by (3) is feasible — that is, if ∑i<hui<~b≤∑i≤hui
and ∑i<haiui+ah(~b−∑i<hui)≤b
— we are done. Moreover, if the right side of either constraint is exceeded, we need to reduce the collection of variables at their upper bound, so we need only consider cases of μ=cj
for j<h
; if, on the other hand, we fall short of ~b
units, we need only consider cases of μ=cj
for j>h
. Thus we can use a bisection search to complete this phase. If we assume a large value of n
, the initial sort can be done in O(nlogn
) time and the remainder of the phase requires O(logn)
iterations, each of which uses O(n)
time.
Unfortunately, I don’t see a way to apply the bisection search to the second phase, in which we look for solutions where the knapsack constraint is binding and λ>0
. We will again search on the value of μ
, but this time monotonically. First apply the greedy heuristic to problem (1), retaining the knapsack constraint but ignoring the count constraint. If the solutions happens by chance to satisfy the count constraint, we are done. In most cases, though, the count constraint will be violated. If the count exceeds ~b
, then we can deduce that the optimal value of μ
in (4) is positive; if the count falls short of ~b
, the optimal value of μ
is negative. We start the second phase with μ=0
and move in the direction of the optimal value.
Since the greedy heuristic sorts items so that c1/a1≥⋯≥cn/an
, and since we are starting with μ=0
, our current sort order has (c1−μ)/a1≥⋯≥(cn−μ)/an
. We will preserve that ordering (resorting as needed) as we search for the optimal value of μ
. To avoid confusion (I hope), let me assume that the optimal value of μ
is positive, so that we will be increasing μ
as we go. We are looking for values of (λ,μ)
where two of the x
variables are basic, which means two have reduced cost 0. Suppose that occurs for xi
and xj
; then
ri=0=rj⟹ci−λai−μ=0=cj−λaj−μ(7)⟹ci−μai=λ=cj−μaj.
It is easy to show (left to the reader as an exercise) that if (c1−μ)/a1≥⋯≥(cn−μ)/an
for the current value of μ
, then the next higher (lower) value of μ
which creates a tie in (7) must involve consecutive a consecutive pair of items (j=i+1
). Moreover, again waving off degeneracy (in this case meaning more than two items with reduced cost 0), if we nudge μ
slightly beyond the value at which items i
and i+1
have reduced cost 0, the only change to the sort order is that items i
and i+1
swap places. No further movement in that direction will cause i
and i+1
to tie again, but of course either of them may end up tied with their new neighbor down the road.
The second phase, starting from μ=0
, proceeds as follows. For each pair (i,i+1)
compute the value μi
of μ
at which (ci−μ)/ai=(ci+1−μ)/ai+1
; replace that value with ∞
if it is less than the current value of μ
(indicating the tie occurs in the wrong direction). Update μ
to miniμi
, compute λ
from (7), and compute x
from (5) and (6). If x
is primal feasible (which reduces to 0≤xi≤ui
and 0≤xi+1≤ui+1
), stop: x
is optimal. Otherwise swap i
and i+1
in the sort order, set μi=∞
(the reindexed items i
and i+1
will not tie again) and recompute μi−1
and μi+1
(no other μj
are affected by the swap).
If the first phase did not find an optimum (and if the greedy heuristic at the start of the second phase did not get lucky), the second phase must terminate with an optimum before it runs out of values of μ
to check (all μj=∞
). Degeneracy can be handled either with a little extra effort in coding (for instance, checking multiple combinations of i
and j
in the second phase when three-way or higher ties occur) or by making small perturbations to break the degeneracy.