What is taught wrongly

A table wider than its input

The knapsack table has (n+1)(W+1) cells and is called polynomial. Adding one character to the input doubles it — across four settings the table grows sixty-four times while the input it is written from grows by half.

The knapsack table has (n+1)(W+1)(n+1)(W+1) cells: one row per item, one column per capacity. It is filled in one pass, every cell does constant work, and the whole thing is routinely described as a polynomial-time algorithm for a problem that is otherwise NP-hard.

Every word of that description is true and the conclusion anybody draws from it is wrong.

nn is a count of things in the input. WW is a number written in the input, and writing a number down takes about log2W\log_2 W characters. So a table proportional to WW is a table proportional to 2(length of one field)2^{\text{(length of one field)}}, and adding one bit to the capacity — one character — doubles the work.

capacity WW bits to write WW cells bits in the whole input
63 6 1,600 318
255 8 6,400 368
1,023 10 25,600 418
4,095 12 102,400 468

The table grew by a factor of 64. The input grew by a factor of 1.47.

Subproblems given a value, against the capacity in the inputBottom-up table at a measured slope of 1.00; Top-down, reachable only at a measured slope of 0.14. 14 items with weights up to 60, against a capacity swept from 200 to 3200. The capacity is a number written in the input rather than a count of things in it, and one of these two series knows that and the other does not.10³10⁴capacity Wsubproblems given a valueBottom-up table · 1.00Top-down, reachable only · 0.14one unit = one subproblem given a valuesubproblems given a value, W from 200 to 3200
Fig. 1 Cells filled against the capacity, on logarithmic axes. The bottom-up table has slope 1.00 in WW — and WW is exponential in the length of the field that holds it, so the same line read against the input length is exponential. The lower series is the same recurrence computed top down, which touches only the capacities some subset of the weights can reach and stops growing entirely once the capacity passes their sum.

What “polynomial time” is a statement about

The definition is precise and the precision is the whole content: an algorithm runs in polynomial time if its cost is bounded by a polynomial in the length of its input as written down.

Not in the number of items. Not in the largest number appearing. In the number of characters it takes to state the problem.

That is not pedantry, and it is not a definition chosen to be awkward. It is chosen so that the class does not depend on how the input happens to be encoded, which it would if the measure were anything else — and the knapsack table is the standard demonstration that the choice matters. Under a binary encoding of the capacity the algorithm is exponential. Under a unary encoding — WW written as WW tally marks — the input length is Θ(W)\Theta(W) and the same algorithm is genuinely polynomial in it.

The algorithm did not change. The encoding did, and the class moved.

An algorithm with this behaviour is called pseudo-polynomial: polynomial in the numeric value of the inputs, exponential in their length. The problems that admit one are called weakly NP-hard, and the distinction is real — there are NP-hard problems, such as three-partition, for which no pseudo-polynomial algorithm exists unless P = NP, so the knapsack’s table is a genuine property of the knapsack and not a general escape.

The knapsack table: 8 items against a capacity of 24Every cell is the best value obtainable from the first i items within a capacity of c. There are 225 of them, and the width of the table is the capacity written in the input rather than the number of things in it.01234567891011121314151617181920212223242/906/162/883/6012/112/468/733/59one unit = one subproblem given a value225 cells = (n+1)(W+1)
Fig. 2 The table at a capacity of 24: 225 cells, and it fits on a page. The whole difficulty is that the picture looks the same at a capacity of 24,000, where it has 225,000 cells and the input describing it is four characters longer.

The unit of the capacity is part of the input

The cleanest way to feel the problem is to change the units and change nothing else.

A knapsack whose capacity is 600 grams and whose weights are in grams has a table 601 columns wide. Express the same physical problem in milligrams — capacity 600,000, weights multiplied by a thousand — and the table has 600,001 columns. The items are the same items, the answer is the same answer, the input is nine characters longer, and the work is a thousand times greater.

Nothing about that is a trick. It is what “proportional to WW” means, and it is why the same algorithm is entirely practical for a scheduling problem measured in whole days and entirely impractical for the same problem measured in milliseconds. The people who run into this do not run into it by choosing adversarial inputs; they run into it by changing a unit.

The related case is money. A change-making or budgeting problem in whole pounds is a small table; the same problem in pence is a table a hundred times wider; the same problem in a currency with a smaller minor unit is wider still. Every one of those is the same problem to the person posing it.

The knapsack table: 6 items against a capacity of 20Every cell is the best value obtainable from the first i items within a capacity of c. There are 147 of them, and the width of the table is the capacity written in the input rather than the number of things in it.012345678910111213141516171819202/905/162/883/6010/110/460000000000000000000000090909090909090909090909090909090909090009090909090106106106106106106106106106106106106106106009090178178178178178194194194194194194194194194194194194009090178178178238238238238238254254254254254254254254254009090178178178238238238238238254254254254254254254254254009090178178178238238238238238254254254254254284284284284one unit = one subproblem given a value147 cells = (n+1)(W+1)
Fig. 3 Six items against a capacity of twenty: 147 cells, and every column is a capacity the algorithm is prepared to answer about. Multiply the units by a thousand and this picture has 120,007 cells and exactly the same six items in it. The rows are the input; the columns are a consequence of how one number was written.

Where this sits among the site’s other complaints about bounds

This is the fourth time on this site that a bound has turned out to be a correct statement about something other than what a reader took it for, and the four are worth putting side by side because the failure is the same shape each time.

What O-notation does not say: a class is a statement about a limit, and the constant it drops can be anything. A limit is not a prediction: the crossover between two classes can sit beyond any size that will ever be run. The block that is not a block: a cost model with unstated parameters produces numbers that are correct in the model and meaningless outside it. And here: a bound is polynomial in a quantity that the reader assumes is the size of the input and is not.

In every case the statement is true. In every case what makes it misleading is a quantity that was not named — the constant, the crossover, the parameters, the encoding — and in every case the repair is the same, which is to name it and measure it.

Cells held at once, against the capacity in the inputBottom-up table at a measured slope of 1.00; Bottom-up, two rows kept at a measured slope of 1.00. 14 items with weights up to 60, against a capacity swept from 200 to 3200. The capacity is a number written in the input rather than a count of things in it, and one of these two series knows that and the other does not.10³10³10⁴capacity Wcells held at onceBottom-up table · 1.00Bottom-up, two rows kept · 1.00one unit = one subproblem given a valuecells held at once, W from 200 to 3200
Fig. 4 And the resource nobody mentions when quoting this bound. Cells held at once, against the capacity: the full table and the rolling frontier are both lines of slope 1, separated by (n+1)/2(n+1)/2. Rolling helps by a constant and does nothing at all about the axis — the width of the table is the problem, and no frontier is narrower than one row.

The memo that is not a memo

There is a second thing that goes wrong on this table and it is a defect rather than a misreading, so it belongs here for a different reason.

Memoising the recursion on the item index alone is a small, natural-looking mistake. The recursion has two arguments; the cache key has one; and the resulting program runs, terminates, and returns a number.

computation cells value returned
the table 1,313 397
memo on the index alone 12 665

Twelve cells instead of thirteen hundred, and an answer that is 68% too high. It is too high rather than too low, which removes the one sanity check somebody might have applied: an optimisation that returns more than the optimum is obviously wrong to anybody who has the optimum, and to anybody who does not it looks like a very good result.

Nothing inside that program can detect it. There is no invariant to violate, no assertion to fire, and no bound to breach. It is caught only by computing the same quantity a second way — which is why this site’s gate runs both and compares, and why the subproblem in a memo must be the whole state the recurrence branches on rather than the argument that happens to look like an index.

The way out, and it changes the question

The table’s width is a number from the input. The escape is to make it a number the algorithm chooses.

Index the table by total value rather than by capacity: cell (i,v)(i, v) holds the least weight needed to reach a value of vv using the first ii items. That table is (n+1)(V+1)(n+1)(V+1) cells with VV the total value, which is exactly as bad as before — until the values are rounded.

Divide every value by εvmax/n\varepsilon \cdot v_{\max} / n and take the floor. The rounded values sum to at most n2/εn^2/\varepsilon, so the table is O(n3/ε)O(n^3/\varepsilon) cells whatever the numbers in the input were. Each item loses less than the rounding unit, at most nn items are taken, so the answer loses at most εvmaxεoptimum\varepsilon \cdot v_{\max} \le \varepsilon \cdot \text{optimum}.

That is a fully polynomial-time approximation scheme, and the reason it works is worth stating in one line: values can be rounded and capacities cannot. Rounding a value changes how good a solution is said to be; rounding a capacity changes which solutions are legal.

Measured on eight items with six-figure values against a capacity of 600, whose exact optimum is 190,515 and whose exact table is 5,409 cells:

ε\varepsilon permitted loss measured loss cells
0.5 50% 1.03% 387
0.25 25% 1.03% 792
0.1 10% 0% 2,025
0.05 5% 0% 4,050

The guarantee permits giving away half the optimum and the scheme gives away one percent. That is the same shape the streaming field measured on a sketch whose bound permitted seven hundred failures and which produced none — the guarantee that is per query — and it carries the same warning. A bound that is loose by a factor of fifty is still a bound, it is still the only thing that holds on every input, and the measured behaviour on one instance is not a licence to assume it.

The gate asserts both directions: the ratio must never fall below 1ε1 - \varepsilon, and some setting in the sweep must lose something. Without the second half the first would have been verified against an instance where rounding happened to cost nothing, which is a check that fires on nothing.

Subproblems given a value, against how little is given away8 items whose values run to six figures, against a capacity of 600. The horizontal axis is 1/ε, so the right-hand end is the setting that gives away least. Rounding the values makes the table's width a function of the number of items and of ε alone, which is why this plate has an axis at all — the exact table's width is a number written in the input.1010³10⁴1/ε — how little is given awaysubproblems given a valueRounded values · 1.01one unit = one subproblem given a valuesubproblems given a value, n from 2 to 40
Fig. 5 Cells against 1/ε1/\varepsilon, at slope 1.00 — the table’s width is proportional to how little is given away, and to nothing else in the input. That is the property the whole construction exists for: an axis that the numbers in the problem cannot move.
The value obtained, against how little is given away8 items whose values run to six figures, against a capacity of 600. The horizontal axis is 1/ε, so the right-hand end is the setting that gives away least. Rounding the values makes the table's width a function of the number of items and of ε alone, which is why this plate has an axis at all — the exact table's width is a number written in the input.101/ε — how little is given awaythe value obtainedRounded values · 0.00Exact table · 0.00one unit = one subproblem given a valuethe value obtained, n from 2 to 40
Fig. 6 And what is obtained, against the same axis, with the exact optimum as the flat line. The approximation reaches it at 1/ε=101/\varepsilon = 10 and stays there, so three quarters of this plate is a scheme paying for a guarantee it did not need on this instance. Which quarter that is cannot be known without the exact answer, which is the thing being avoided.

Both of the alternatives above are worth having in mind while reading the defence, because they change what it is a defence of: not the table, but the decision to reach for it first.

The defence, and what is left of it

There is a reasonable answer to all of this and it deserves stating properly, because the pseudo-polynomial table is genuinely used and the people using it are not confused.

The defence is that in practice the numbers are small. A knapsack over a few hundred items with a capacity in the thousands is a table of a few hundred thousand cells, which is nothing, and the exponential in the encoding never gets a chance to bite because the encoding never gets long. Under that reading the bound Θ(nW)\Theta(nW) is exactly the right thing to quote, because WW is a quantity the person posing the problem knows.

What is left of the objection after that is two things and both are practical.

It fails silently and suddenly. The behaviour is linear in WW with a small constant right up to the point where the table no longer fits in memory, and then it stops. There is no gradual degradation to warn anybody, and the parameter that crosses the threshold is often not the one being varied — a change of units, a finer-grained resource, a merged data set with one large entry.

The top-down form does not have the problem, on many instances. The reachable capacities are the subset sums of the weights, and there are at most 2n2^n of them but usually far fewer, bounded by the total weight. Measured, the top-down cell count stops growing at 2,157 while the bottom-up one goes on doubling — so on that family of instances the recurrence is not pseudo-polynomial at all and the strategy was what made it so.

That is the more useful conclusion than the taxonomy. The problem is weakly NP-hard and the exact table is exponential in the encoding, and both of those are facts about the worst case; what an implementation actually costs is decided by which capacities its own weights can reach, and that is measurable on the instance in hand.

The other direction the same complaint runs

There is a mirror image of this defect and it is worth naming, because a reader who has taken the point above will meet it and mistake it for the same thing.

A bound stated in the length of the encoding can be misleading in the opposite direction: an algorithm that is exponential in the length of a number can be entirely practical when that number is small, and an algorithm that is polynomial in the length can be useless when the polynomial has a large degree or a large constant. “Polynomial time” is a class boundary chosen because it is robust to the encoding and to the machine, not because it is a prediction that a program will finish.

This site has made that point before at the other end of the scale — a limit is not a prediction measured a crossover past any size that will ever be run — and the two complaints are the same complaint. A class is a statement about how a cost moves as one stated quantity grows. It is silent about the constant, silent about the range, and silent about which quantity is being grown. Naming that quantity is the whole of the discipline, and it is why every plate on this site prints its own axis.

The other exponential, and choosing between them

The table is exponential in the length of the capacity field. There is a completely different exact method that is exponential in the number of items and does not contain WW at all, and having both turns a lament into a decision.

Split the items into two halves. Enumerate every subset of the first half, recording its weight and value; do the same for the second. Sort one list by weight, and for each subset of the other half find the best-value partner that fits in the remaining capacity. That is about 2n/22^{n/2} subsets on each side plus a sort, and its cost mentions no number from the input — only how many items there are.

So the two exact methods are exponential in different things:

method cost exponential in
the table Θ(nW)\Theta(nW) the length of the capacity
meet in the middle Θ(n2n/2)\Theta(n \cdot 2^{n/2}) the number of items

and the crossing between them is at roughly W2n/2W \approx 2^{n/2}. Forty items against a capacity of a thousand is a table of forty thousand cells and a subset enumeration of a million: the table wins easily. Forty items against a capacity of a billion is a table of forty billion cells and the same million: the enumeration wins by four orders of magnitude.

Both are exact, both are exponential, and which exponential bites is decided by two numbers the caller has in hand before choosing. That is a much better position than the one the pseudo-polynomial framing suggests, where the table is presented as the dynamic-programming answer and the alternative is not mentioned.

It also sharpens what “weakly NP-hard” is telling anybody. It says a pseudo-polynomial algorithm exists — not that it is the one to run. On an instance with few items and enormous numbers the pseudo-polynomial algorithm is the slow one, and the method with the frankly exponential bound is the practical choice.

Capacities can be rounded, if overfilling is allowed

The rule stated above — values can be rounded and capacities cannot — is exactly right for the problem as posed, and it is worth noticing what happens when the posing is relaxed.

Rounding a capacity changes which solutions are legal, so an algorithm that rounds it returns a solution that may not fit. If the constraint is physical — a container, a vehicle, a page — that is a wrong answer and there is nothing more to say. If the constraint is a budget, a quota or a time allowance, a solution that exceeds it by a stated fraction may be perfectly acceptable, and then the capacity axis can be scaled exactly as the value axis is.

That gives a bicriteria approximation: within ε\varepsilon of the optimal value, using at most (1+ε)(1+\varepsilon) of the capacity, with a table whose width no number in the input can move. It is a weaker guarantee in one dimension and a stronger one in another, and which of the three schemes to use is settled by a question about the application rather than about the algorithm.

Naming it is worth a paragraph because of the pattern rather than the technique. A resource that cannot be approximated is often a resource whose constraint was stated more strictly than the situation required, and the question “what happens if this bound is exceeded by one per cent” is worth asking before concluding that an axis is untouchable.

What to take from it

Three things, and none of them is about the knapsack.

Name the nn. A bound is a function of something, and on this site every plate says what. “Polynomial in nn and WW” is a complete statement and “polynomial” is not, and the gap between them is where the whole of this essay lives.

A table’s dimensions come from the state, and the state can be chosen. Capacity and total value index the same problem, they are both correct, and one of them has an axis that can be rounded. Recognising that a recurrence has more than one possible state is the move that produced the approximation scheme, and it is not visible from a bound.

A cheaper answer with no way to check it is not cheaper. The index-only memo is 109 times smaller than the correct table and it is not an optimisation, it is a different function. The site’s method here is the one it applies everywhere: compute the same quantity by machinery that does not share the mistake, and compare.

Subproblems given a value, against the capacity in the inputBottom-up table at a measured slope of 1.00; Top-down, reachable only at a measured slope of 0.14; Bottom-up, two rows kept at a measured slope of 1.00. 14 items with weights up to 60, against a capacity swept from 200 to 3200. The capacity is a number written in the input rather than a count of things in it, and one of these two series knows that and the other does not.10³10⁴capacity Wsubproblems given a valueBottom-up table · 1.00Top-down, reachable only · 0.14Bottom-up, two rows kept · 1.00one unit = one subproblem given a valuesubproblems given a value, W from 200 to 3200
Fig. 7 The three exact strategies on one axis, since the whole essay is about that axis. Two of them have slope 1 in the capacity; the top-down one flattens completely, because the reachable capacities are decided by the weights and there are only so many subset sums. It is the same recurrence in all three cases, and one of them is not pseudo-polynomial on this instance at all.
Cells held at once, against how little is given away8 items whose values run to six figures, against a capacity of 600. The horizontal axis is 1/ε, so the right-hand end is the setting that gives away least. Rounding the values makes the table's width a function of the number of items and of ε alone, which is why this plate has an axis at all — the exact table's width is a number written in the input.1010³10⁴1/ε — how little is given awaycells held at onceRounded values · 1.01one unit = one subproblem given a valuecells held at once, n from 2 to 40
Fig. 8 The approximation’s other axis, for completeness: cells held at once against 1/ε1/\varepsilon, at slope 1.00 like the work. Rounding the values bounds the space by the same argument that bounds the time, and the rolling frontier applies here exactly as it does to the exact table — two rows of a table whose width is n2/εn^2/\varepsilon rather than WW. Every technique in this phase composes with every other, which is the one general thing the phase has to offer.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

The objects this essay names

Each one links to every other essay that touches it.

ApproximationComplexity classCost modelDynamic programmingEncodingKnapsackLower boundMeasured countMemoisationPseudo polynomialSubproblemTrade offUpper bound