Sensitivity Analysis

Work in progress. This note is still being written and incomplete.

Sensitivity analysis studies how the optimal solution reacts to changes in the LP data, without solving the LP again from the start.

Current Cost

The current cost cjc_j of a variable xjx_j is its coefficient in the original objective function, before any pivoting.

  • Slack and surplus variables
    Cost 0 always, since they track unused capacity or excess, not a produced quantity. x14x_1 \le 4 becomes x1+s1=4x_1 + s_1 = 4, and cs1=0c_{s_1} = 0.
  • Artificial variables
    Added only to satisfy a \ge or == constraint, and carry a penalty cost so the simplex method drives them out of the basis. x1+x24x_1 + x_2 \ge 4 becomes x1+x2s1+a1=4x_1 + x_2 - s_1 + a_1 = 4, with ca1=Mc_{a_1} = -M in the Big-M method for a maximization problem, or ca1=1c_{a_1} = 1 in phase 1 of the two-phase method.

Reduced Cost

The reduced cost of a variable xjx_j is its entry in the objective row of the current tableau. It is the amount zz would change per unit of xjx_j brought into the basis.

A basic variable always has reduced cost 0 in its own column. Only non-basic variables have a reduced cost worth tracking.

Shadow Price

The shadow price of constraint ii is sis_i‘s own entry in the objective row, the amount zz changes per unit increase in bib_i.

Example

Take the LP solved in the simplex worked example:

maximizez=3x1+5x2subject tox142x2123x1+2x218x1,x20\begin{aligned} \text{maximize} \quad & z = 3x_1 + 5x_2 \\ \text{subject to} \quad & x_1 \le 4 \\ & 2x_2 \le 12 \\ & 3x_1 + 2x_2 \le 18 \\ & x_1, x_2 \ge 0 \end{aligned}

Converting to standard form adds a slack s1,s2,s3s_1, s_2, s_3 to each constraint, one per row, with cost 0 in the objective, since a slack represents unused capacity rather than a produced quantity: z=3x1+5x2+0s1+0s2+0s3z = 3x_1 + 5x_2 + 0s_1 + 0s_2 + 0s_3.

Its optimal tableau has basis (s1,x2,x1)(s_1, x_2, x_1), solution x1=2x_1 = 2, x2=6x_2 = 6, z=36z = 36:

Basicx1x_1x2x_2s1s_1s2s_2s3s_3RHS
zz0003/2136
s1s_10011/3-1/32
x2x_20101/206
x1x_1100-1/31/32

The current costs are c1=3c_1 = 3, c2=5c_2 = 5, and cs1=cs2=cs3=0c_{s_1} = c_{s_2} = c_{s_3} = 0, read straight off the objective function above. The reduced costs are the objective row of the tableau: 0 for the basic variables (s1,x2,x1s_1, x_2, x_1) and 3/23/2 for s2s_2 and 11 for s3s_3.

Every case below reuses this tableau. A change to the LP keeps the basis (s1,x2,x1)(s_1, x_2, x_1) optimal as long as 2 conditions still hold:

  • Feasibility
    Every basic variable’s value in the RHS column stays 0\ge 0.
  • Optimality
    Every reduced cost, the objective row entry of every non-basic variable, stays 0\ge 0.

s2s_2 and s3s_3 already satisfy optimality (3/203/2 \ge 0 and 101 \ge 0), and all 3 basic values already satisfy feasibility (2,6,202, 6, 2 \ge 0). If a change breaks either condition, the tableau is repaired with primal or dual simplex instead of restarting.

Change in the Right Hand Side

Changing constraint ii‘s RHS bib_i by Δ\Delta shifts every basic variable’s value by Δ\Delta times that variable’s entry in sis_i‘s column of the tableau. sis_i’s column is the right thing to use here because sis_i started as the only variable with a coefficient in row ii, before any pivoting.

Underutilized Constraint

Constraint 1 is x14x_1 \le 4. Its slack s1s_1 is basic at value 2, so constraint 1 has 2 units of unused capacity.

s1s_1’s column is (1,0,0)(1, 0, 0) for rows s1,x2,x1s_1, x_2, x_1. Only the s1s_1 row has a non-zero entry there, so only s1s_1 moves when b1b_1 changes by Δ\Delta:

(262)+Δ(100)=(2+Δ62)\begin{pmatrix}2\\6\\2\end{pmatrix} + \Delta \begin{pmatrix}1\\0\\0\end{pmatrix} = \begin{pmatrix}2 + \Delta\\6\\2\end{pmatrix}

x1x_1 and x2x_2 do not move, because constraint 1 was not binding to begin with. Feasibility only fails once the spare capacity runs out, at Δ=2\Delta = -2:

2+Δ0    Δ22 + \Delta \ge 0 \implies \Delta \ge -2

There is no upper bound, since only the s1s_1 row changes. So b1b_1, originally 4, can range over [2,)[2, \infty) without disturbing x1=2x_1 = 2, x2=6x_2 = 6, or z=36z = 36. Drag Δ\Delta below 2-2 to see the basis break down:

Basic x1x_1x2x_2s1s_1s2s_2s3s_3 RHS
zz 0000003/23/211 36
s1s_1 0000111/31/31/3-1/3 2
x2x_2 0011001/21/200 6
x1x_1 1100001/3-1/31/31/3 2
0
Feasible. Basis stays optimal.

The objective moves by s1s_1‘s own objective row entry, its shadow price, times Δ\Delta. That entry is 0, confirming constraint 1 is non-binding: changing its unused capacity, within the range above, does not change zz at all.

Fully Utilized Constraint

Constraint 3 is 3x1+2x2183x_1 + 2x_2 \le 18. At x1=2x_1 = 2, x2=6x_2 = 6 it reads 3(2)+2(6)=183(2) + 2(6) = 18, exactly the limit, so its slack s3s_3 is non-basic at 0 with no spare capacity to give.

s3s_3 has no row of its own in the tableau, since it is non-basic, but its column is still read straight off the tableau: (1/3,0,1/3)(-1/3, 0, 1/3) for rows s1,x2,x1s_1, x_2, x_1. Both s1s_1 and x1x_1 have non-zero entries there, so both move when b3b_3 changes by Δ\Delta:

(262)+Δ(1/301/3)=(2Δ/362+Δ/3)\begin{pmatrix}2\\6\\2\end{pmatrix} + \Delta \begin{pmatrix}-1/3\\0\\1/3\end{pmatrix} = \begin{pmatrix}2 - \Delta/3\\6\\2 + \Delta/3\end{pmatrix}

Unlike the underutilized case, x1x_1 itself moves this time, because constraint 3 was binding. Feasibility needs both altered rows to stay 0\ge 0:

2Δ30    Δ62+Δ30    Δ62 - \frac{\Delta}{3} \ge 0 \implies \Delta \le 6 \qquad 2 + \frac{\Delta}{3} \ge 0 \implies \Delta \ge -6

So b3b_3, originally 18, can range over [12,24][12, 24]. Drag Δ\Delta past either edge to see the basis break down:

Basic x1x_1x2x_2s1s_1s2s_2s3s_3 RHS
zz 0000003/23/211 36
s1s_1 0000111/31/31/3-1/3 2
x2x_2 0011001/21/200 6
x1x_1 1100001/3-1/31/31/3 2
0
Feasible. Basis stays optimal.

The objective moves by s3s_3‘s own objective row entry, its shadow price, times Δ\Delta. That entry is 1, so zz changes 1-for-1 with Δ\Delta inside the range above, confirming constraint 3 is binding: its capacity is worth exactly 1 unit of zz per unit.

Change in a Cost Coefficient

Non-Basic Variable

s2s_2 is non-basic, with reduced cost 3/23/2 and current cost cs2=0c_{s_2} = 0, its coefficient in z=3x1+5x2+0s1+0s2+0s3z = 3x_1 + 5x_2 + 0s_1 + 0s_2 + 0s_3.

Changing cs2c_{s_2} only changes s2s_2‘s own reduced cost, by the same amount in the opposite direction. If cs2c_{s_2} increases by Δ\Delta, the reduced cost drops to 3/2Δ3/2 - \Delta, needing

32Δ0    Δ32\frac32 - \Delta \ge 0 \implies \Delta \le \frac32

So cs2c_{s_2} can range over (,3/2](-\infty, 3/2] before s2s_2 becomes attractive enough to enter the basis. Drag Δ\Delta past 3/23/2 to see the basis break down:

Basic x1x_1x2x_2s1s_1s2s_2s3s_3 RHS
zz 000000 1.5 1 36
0
Optimal. Basis stays optimal.

Basic Variable

x1x_1 is basic with c1=3c_1 = 3. x1x_1’s row runs through every column of the tableau, so changing c1c_1 changes every non-basic reduced cost, not just its own.

Set c1=3+Δc_1 = 3 + \Delta. x1x_1’s row gives entry 1/3-1/3 under s2s_2 and 1/31/3 under s3s_3, so each reduced cost changes by Δ\Delta times that entry:

32+Δ(13)0    Δ921+Δ(13)0    Δ3\frac32 + \Delta\left(-\frac13\right) \ge 0 \implies \Delta \le \frac92 \qquad 1 + \Delta\left(\frac13\right) \ge 0 \implies \Delta \ge -3

Both together give 3Δ9/2-3 \le \Delta \le 9/2, so c1c_1 can range over [0,7.5][0, 7.5].

On paper this is the row operation z-rowz-row+Δx1’s rowz\text{-row} \leftarrow z\text{-row} + \Delta \cdot x_1\text{'s row}: scale x1x_1‘s row by Δ\Delta and add it into the objective row, the same mechanical step a pivot uses. The highlighted row below is the one being scaled, the highlighted cells are where it lands. Drag Δ\Delta past either edge to see which reduced cost breaks first:

z-rowz-row+Δz\text{-row} \leftarrow z\text{-row} + \Delta \cdot x1’s rowx_1\text{'s row}

Basic x1x_1x2x_2s1s_1s2s_2s3s_3 RHS
zz 000000 3/2 1 36
s1s_1 0000111/31/31/3-1/3 22
x2x_2 0011001/21/200 66
x1x_1 1100001/3-1/31/31/3 22
0
Optimal. Basis stays optimal.

Addition of a Variable

Suppose a new activity x3x_3 becomes available, with cost c3=4c_3 = 4 and column (1,1,1)(1, 1, 1), one unit of each resource per unit produced.

Transform its column with the basis inverse read off the s1,s2,s3s_1, s_2, s_3 columns above:

(11/31/301/2001/31/3)(111)=(11/20)\begin{pmatrix} 1 & 1/3 & -1/3 \\ 0 & 1/2 & 0 \\ 0 & -1/3 & 1/3 \end{pmatrix} \begin{pmatrix}1\\1\\1\end{pmatrix} = \begin{pmatrix}1\\1/2\\0\end{pmatrix}

Its reduced cost, using cB=(0,5,3)c_B = (0, 5, 3) for the current basis (s1,x2,x1)(s_1, x_2, x_1):

(0)(1)+(5)(12)+(3)(0)4=32<0(0)(1) + (5)\left(\frac12\right) + (3)(0) - 4 = -\frac32 < 0
  • Reduced cost 0\ge 0
    The current solution stays optimal. The new variable stays non-basic at value 0.
  • Reduced cost <0< 0
    Optimality is violated, as it is here. x3x_3 can enter the basis and improve zz beyond 36.

Drag cnewc_{\text{new}} past 5/25/2 to see the reduced cost flip sign:

znewcnewz_{\text{new}} - c_{\text{new}} = 2.5 - cnewc_{\text{new}} 2.5
0
Reduced cost ≥ 0. The new variable stays non-basic at value 0.

Addition of a Constraint

Suppose a new limit x1+x2kx_1 + x_2 \le k is imposed. The current optimum gives x1+x2=2+6=8x_1 + x_2 = 2 + 6 = 8.

If current optimal solution satisfies the new constraint, the solution stays optimal.

Otherwise, the constraint is added to the optimal tableau with a slack, surplus, or artificial variable, and row-reduced against the current basis. The new basic variable gets a negative value, so the tableau is optimal but infeasible. Continue with the dual simplex method.

At k=5k = 5, 8>58 > 5 violates it, falling into the second case. Drag kk below 8 to see the constraint start to bind:

x1+x2x_1 + x_2 at the current optimum 8
12
Satisfied. The optimum stays as is.

Continued with the dual simplex method in the dual simplex worked example, reaching the new optimum x1=0x_1 = 0, x2=5x_2 = 5, z=25z = 25 for k=5k = 5.

Written by September 13, 2026 8 min read
Was this helpful?