SpamCo manufactures two types of potted meat. Type 1 sells for $3 per pot and requires 2 units of animal byproducts and 3 hours to cure. Type 2 sells for $4 per pot and requires 3 units of byproduct and 1 hour to cure. There are 10 units of byproduct and 6 hours for curing available. How can SpamCo maximize its profit?
maximize: z = 3x1 + 4x2 profit
subject to: 2x1 + 3x2 <= 10 animal byproducts
3x1 + x2 <= 6 time for curing
x1, x2 >= 0
The feasible set has extreme points (0, 0), (0, 10/3), (8/7, 18/7) and (2, 0).
If we move the objective line up and to the right, the last point it touches, (8/7, 18/7), is the optimal solution with objective value 96/7.
In general LP can be solved in polynomial time. Assuming an LP is feasible and bounded, Simplex, the most common method, looks roughly like:
Current software solves LP with millions of variables.
Disclaimer: this is only a very small part of a large story.
We have n items, each with value vi and weight wi. We can carry b in total weight. What combination of items should we pack to maximize value?
maximize: z = Σ vi * xi
subject to: Σ wi * xi <= b
xi in {0, 1} for all i
Disclaimer: IANACT (I am not a complexity theorist)
"NP-Hard is a term we use to scare graduate students."
Overheard at INFORMS Conference 2008
Consider the following instance of the Travelling Salesman Problem:

Add 995 points along the perimeter of the pentagon Now you have an easy, 1,000-city TSP.

Add constraints that make the LP have (more) integral extreme points.
If all extreme points are integral, then Simplex will return an optimal integer point.
If the LP relaxation returns fractional variables, divide it into two LPs.
Discard the fractional area.
Now we have two sub-LPs, each with a dual bound. We can use this to guide our search.
Sometimes we can make logical implications about integer variables just by looking at the model. Consider:
x1 + x2 >= 1
x1 + x2 + x3 <= 1
xi in {0, 1} for all i
We can set x3 to zero and remove it from our problem entirely. (Why?)
All these techniques (and many more) exist in commercial and open source optimization solvers.
The ZIB Optimization Suite is a set of non-commercial tools for solving MIPs. It contains:
The ZIMPL version of our model is a lot like the algebraic one:
var x1 >= 0; # pots of type 1 var x2 >= 0; # pots of type 2 maximize profit: 3*x1 + 4*x2; subto byproducts: 2*x1 + 3*x2 <= 10; subto curing: 3*x1 + x2 <= 6;
from zibopt import scip
solver = scip.solver()
x1 = solver.variable() # pots of type 1
x2 = solver.variable() # pots of type 2
solver += 2*x1 + 3*x2 <= 10 # animal byproducts
solver += 3*x1 + x2 <= 6 # curing time
solution = solver.maximize(objective=3*x1 + 4*x2)
if solution:
print 'profit:', solution.objective
print 'pots of type 1:', solution[x1]
print 'pots of type 2:', solution[x2]
else:
print 'invalid problem'
We can model Sudoku as a Binary Integer Program (BIP) with 9*9*9 = 729 variables.
# 0 indicates a cell value is not given
problem = [
[0, 0, 0, 6, 9, 2, 0, 4, 0],
[7, 0, 0, 0, 0, 0, 8, 9, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 6],
[0, 0, 9, 0, 1, 7, 0, 0, 3],
[0, 0, 7, 0, 8, 0, 5, 0, 0],
[8, 0, 0, 4, 6, 0, 1, 0, 0],
[5, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 8, 6, 0, 0, 0, 0, 0, 1],
[0, 3, 0, 7, 2, 8, 0, 0, 0]
]
Indexes for rows, columns, 3x3 groups, and values cells can take on:
# Indexes for creating variables and constraints rows = range(len(problem)) cols = rows vals = range(1, 10)
9x9x9 structure for storing our binary variables. .
x = dict((i, dict((j, {}) for j in cols)) for i in rows)
Create a variable for each cell and value. If row i, column j requires value k, then x[i][j][k] = 1.
from itertools import product
from zibopt import scip
# Initialize one binary variable per cell value
solver = scip.solver()
for i, j, k in product(rows, cols, vals):
x[i][j][k] = solver.variable(
vartype = scip.BINARY,
lower = 1 if problem[i][j] == k else 0
)
Each cell takes exactly one value:
for i, j in product(rows, cols):
solver += sum(x[i][j][k] for k in vals) == 1
Each value occurs in each row once:
for i, k in product(rows, vals):
solver += sum(x[i][j][k] for j in cols) == 1
Each value occurs in each column once:
for j, k in product(cols, vals):
solver += sum(x[i][j][k] for i in rows) == 1
Each 3x3 group contains each value once:
groups = (0, 3, 6)
for m, n, k in product(groups, groups, vals):
solver += sum(
x[i][j][k] for i, j in product(
range(m,m+3), range(n,n+3)
)
) == 1
Find the solution and fill in our problem array with appropriate values.
solution = solver.maximize()
if solution:
for i, j in product(rows, cols):
for k in vals:
if solution[x[i][j][k]]:
problem[i][j] = k
break
for row in problem:
print row
else:
print 'invalid'
[3, 1, 8, 6, 9, 2, 7, 4, 5] [7, 6, 4, 3, 5, 1, 8, 9, 2] [2, 9, 5, 8, 7, 4, 3, 1, 6] [6, 2, 9, 5, 1, 7, 4, 8, 3] [1, 4, 7, 2, 8, 3, 5, 6, 9] [8, 5, 3, 4, 6, 9, 1, 2, 7] [5, 7, 2, 1, 4, 6, 9, 3, 8] [4, 8, 6, 9, 3, 5, 2, 7, 1] [9, 3, 1, 7, 2, 8, 6, 5, 4]