Workspace IndexAlgorithms › Day 13

Linear Programming and Duality Intuition TODO

Algorithms · Day 13 / 100 · A. Advanced Algorithms & Data Structures (Day 1-19)

Concept

Linear programming (LP) maximizes or minimizes a linear objective function subject to linear inequality constraints; the feasible region is a convex polytope, and if an optimum exists, it's achieved at one of its vertices. Every LP has a paired dual problem: weak duality says any feasible dual solution bounds the primal optimum, and strong duality says that when both sides are feasible, the two optimal values coincide. A dual variable represents the shadow price of its constraint — how much the objective improves if that constraint is relaxed by one unit. Complementary slackness ties the two together: a constraint with slack left over has a shadow price of 0, and any constraint with a positive price must be tight. So in an allocation problem, the primal answers who gets how much, while the dual simultaneously produces the prices that support that allocation.

In problems that divide up a scarce resource — auctions, blockspace allocation — the proof that an allocation is optimal and the price charged to participants come out of the same dual structure, and knowing that is what lets you settle an argument with numbers instead of opinions.

Code & Formula

# Day 13: 선형계획과 쌍대성 직관 — 경매·배분 문제의 원문제/쌍대문제
# 2변수 LP를 꼭짓점 열거로 풀고, 쌍대 LP도 같은 방식으로 풀어 강쌍대성(최적값 일치)을 확인한다.

def line_intersections(constraints):
    pts = []
    for i in range(len(constraints)):
        for j in range(i + 1, len(constraints)):
            a1, b1, _, c1 = constraints[i]
            a2, b2, _, c2 = constraints[j]
            det = a1 * b2 - a2 * b1
            if abs(det) < 1e-9:
                continue
            pts.append(((c1 * b2 - c2 * b1) / det, (a1 * c2 - a2 * c1) / det))
    return pts

def feasible(pt, constraints, tol=1e-6):
    x, y = pt
    for a, b, op, c in constraints:
        val = a * x + b * y
        if op == "<=" and val > c + tol:
            return False
        if op == ">=" and val < c - tol:
            return False
    return True

def solve_lp_2d(constraints, obj, maximize):
    candidates = [p for p in line_intersections(constraints) if feasible(p, constraints)]
    key = lambda p: obj[0] * p[0] + obj[1] * p[1]
    best = max(candidates, key=key) if maximize else min(candidates, key=key)
    return best, key(best)

# 원문제: maximize x + 2y  s.t.  x+y<=4, x+3y<=6, x,y>=0  (자원 배분: 두 재화를 두 제약 아래 최대화)
primal_constraints = [(1, 1, "<=", 4), (1, 3, "<=", 6), (1, 0, ">=", 0), (0, 1, ">=", 0)]
p_pt, p_val = solve_lp_2d(primal_constraints, (1, 2), maximize=True)

# 쌍대문제: minimize 4u + 6v  s.t.  u+v>=1, u+3v>=2, u,v>=0  (쌍대변수 = 각 제약의 잠재가격)
dual_constraints = [(1, 1, ">=", 1), (1, 3, ">=", 2), (1, 0, ">=", 0), (0, 1, ">=", 0)]
d_pt, d_val = solve_lp_2d(dual_constraints, (4, 6), maximize=False)

print(f"원문제 최적해 (x,y) = ({p_pt[0]:.2f}, {p_pt[1]:.2f}), 최적값 = {p_val:.2f}")
print(f"쌍대문제 최적해 (u,v) = ({d_pt[0]:.2f}, {d_pt[1]:.2f}), 최적값 = {d_val:.2f}")
print(f"강쌍대성 확인 (원문제 최적값 == 쌍대문제 최적값): {abs(p_val - d_val) < 1e-6}")

Exercise

Formulate a small allocation problem with 5 to 10 bids as an LP, solve it with a solver, extract the dual values, then slightly relax one constraint and confirm the change in the objective matches its dual value.

Practical Connection

Batching orders and matching them at a single uniform clearing price can be expressed as an LP where the dual price is exactly that clearing price — a useful exercise for re-reading Verex's CLOB matching logic through an optimization lens.

If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.


한국어

선형계획과 쌍대성 직관 TODO

Algorithms · Day 13 / 100 · A. 고급 알고리즘·자료구조 (Day 1–19)

경매·MEV 배분의 언어

개념

선형계획(LP)은 선형 부등식 제약 아래에서 선형 목적함수를 최대화하거나 최소화하는 문제이며, 실행가능 영역은 볼록 다면체이고 최적해가 존재하면 그 꼭짓점에서 달성된다. 모든 LP에는 짝이 되는 쌍대(dual) 문제가 있고, 약쌍대성은 임의의 쌍대 실행가능해가 원문제 최적값의 한계를 준다는 것, 강쌍대성은 양쪽이 모두 실행가능하면 두 최적값이 일치한다는 것이다. 쌍대 변수는 각 제약의 잠재가격(shadow price), 즉 그 제약을 한 단위 완화했을 때 목적값이 얼마나 개선되는지를 뜻한다. 상보여유 조건은 여유가 남는 제약의 잠재가격은 0이고, 양의 가격이 붙은 제약은 반드시 타이트하다는 관계를 말한다. 그래서 배분 문제에서 원문제는 누구에게 얼마를 줄지를, 쌍대는 그 배분을 뒷받침하는 가격을 동시에 내놓는다.

경매나 블록스페이스 배분처럼 희소 자원을 나누는 문제에서, 배분의 최적성 증명과 참가자에게 물릴 가격이 같은 쌍대 구조에서 나온다는 사실을 알아야 논쟁을 수치로 끝낼 수 있다.

코드 · 수식

# Day 13: 선형계획과 쌍대성 직관 — 경매·배분 문제의 원문제/쌍대문제
# 2변수 LP를 꼭짓점 열거로 풀고, 쌍대 LP도 같은 방식으로 풀어 강쌍대성(최적값 일치)을 확인한다.

def line_intersections(constraints):
    pts = []
    for i in range(len(constraints)):
        for j in range(i + 1, len(constraints)):
            a1, b1, _, c1 = constraints[i]
            a2, b2, _, c2 = constraints[j]
            det = a1 * b2 - a2 * b1
            if abs(det) < 1e-9:
                continue
            pts.append(((c1 * b2 - c2 * b1) / det, (a1 * c2 - a2 * c1) / det))
    return pts

def feasible(pt, constraints, tol=1e-6):
    x, y = pt
    for a, b, op, c in constraints:
        val = a * x + b * y
        if op == "<=" and val > c + tol:
            return False
        if op == ">=" and val < c - tol:
            return False
    return True

def solve_lp_2d(constraints, obj, maximize):
    candidates = [p for p in line_intersections(constraints) if feasible(p, constraints)]
    key = lambda p: obj[0] * p[0] + obj[1] * p[1]
    best = max(candidates, key=key) if maximize else min(candidates, key=key)
    return best, key(best)

# 원문제: maximize x + 2y  s.t.  x+y<=4, x+3y<=6, x,y>=0  (자원 배분: 두 재화를 두 제약 아래 최대화)
primal_constraints = [(1, 1, "<=", 4), (1, 3, "<=", 6), (1, 0, ">=", 0), (0, 1, ">=", 0)]
p_pt, p_val = solve_lp_2d(primal_constraints, (1, 2), maximize=True)

# 쌍대문제: minimize 4u + 6v  s.t.  u+v>=1, u+3v>=2, u,v>=0  (쌍대변수 = 각 제약의 잠재가격)
dual_constraints = [(1, 1, ">=", 1), (1, 3, ">=", 2), (1, 0, ">=", 0), (0, 1, ">=", 0)]
d_pt, d_val = solve_lp_2d(dual_constraints, (4, 6), maximize=False)

print(f"원문제 최적해 (x,y) = ({p_pt[0]:.2f}, {p_pt[1]:.2f}), 최적값 = {p_val:.2f}")
print(f"쌍대문제 최적해 (u,v) = ({d_pt[0]:.2f}, {d_pt[1]:.2f}), 최적값 = {d_val:.2f}")
print(f"강쌍대성 확인 (원문제 최적값 == 쌍대문제 최적값): {abs(p_val - d_val) < 1e-6}")

연습

입찰 5~10건짜리 소규모 배분 문제를 LP로 세워 solver로 풀고 쌍대값을 뽑은 뒤, 제약 하나를 아주 조금 완화했을 때 목적값 변화가 그 쌍대값과 일치하는지 확인하기.

실무 · Verex 연결

배치 방식으로 주문을 모아 균일 청산가로 체결하는 문제는 LP로 표현할 수 있고 쌍대가격이 곧 청산가에 해당하므로, Verex의 CLOB 체결 로직을 최적화 관점에서 다시 읽어 보는 훈련이 된다.

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 12. 최대 유량·최소 컷과 매칭14. 랜덤화·근사 알고리즘 →