Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions algorithms/RodCutting.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Rod Cutting Problem

Given a rod of length $n$ and a table of prices $P$ for different lengths,
determine the maximum revenue $r_n$ obtainable by cutting up the rod and
selling the pieces.

Formal definition:
Let $r_i$ be the maximum revenue for a rod of length $i$.
$
r_n = cases(
0 & "if " n = 0,
max_(1 <= i <= n) (P_i + r_(n-i)) & "if " n > 0
)
$

*As mapcode:*

Here, $NN$ is the set of non-negative integers $\{0, 1, ...\}$,
and $NN_bot = NN union {bot}$.

_primitives_: `max`, `sum`($+$)

$
I &= P:vec(NN) times n:NN quad quad quad "where" m = |P| \
X_n &= [0..n] -> NN_bot quad quad quad \
A &= NN \
rho(n) &= { i -> bot | i in {0 dots n}} \
F_P(x_i) &= cases(
0 & "if " i = 0,
max_(1 <= j <= i) (P_(j-1) + x_(i-j)) & "if " i > 0 " and " j-1 < m
)\
pi_n(x) &= x_n
$

// Price table: P[0] = price for length 1, P[1] = price for length 2, etc.
// So for this example: length 1 costs 1, length 2 costs 5, length 3 costs 8, length 4 costs 9
#let inst_P = (1, 5, 8, 9);
// Target length
#let inst_n = 4;

#figure(
caption: [Rod Cutting DP table for $n = #inst_n$ and prices $P = #inst_P$.],
$
#{
let rho = (inst) => {
let x = ()
for i in range(0, inst + 1) {
x.push(none)
}
x
}

let F_i = (P) => (x) => ((i,)) => {
if i == 0 {
0
} else if x.at(i) != none {
// Already computed, keep the value
x.at(i)
} else {
// Check if all dependencies are computed
let all_deps_ready = true
for j in range(1, i + 1) {
if j - 1 < P.len() and x.at(i - j) == none {
all_deps_ready = false
break
}
}

if not all_deps_ready {
none
} else {
let max_rev = 0
// j is the length of the first cut
for j in range(1, i + 1) {
// Check if we have a price for this cut
if j - 1 < P.len() {
let p_j = P.at(j - 1)
let r_remaining = x.at(i - j)
max_rev = calc.max(max_rev, p_j + r_remaining)
}
}
max_rev
}
}
}
let F = map_tensor(F_i(inst_P), dim: 1)

let pi = (i) => (x) => x.at(i)

// X_h visualizer from fibonacci.typ
let X_h = (x, diff_mask: none) => {
let cells = x.enumerate().map(((i, x_i)) => {
let val = if x_i != none {[$#x_i$]} else {[$bot$]}
if diff_mask != none and diff_mask.at(i) {
// changed element: highlight
rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$]
} else {
rect(stroke: none, inset: 2pt)[$#val$]
}
})
$vec(delim: "[", ..cells)$
}

mapcode-viz(
rho, F, pi(inst_n),
X_h: X_h,
pi_name: [$mpi (#inst_n)$],
group-size: inst_n + 1, // Show all steps in one row
cell-size: 10mm,
scale-fig: 95%
)(inst_n)
}
$
)
139 changes: 139 additions & 0 deletions algorithms/SubsetSum.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Subset Sum Problem

Given a set of non-negative integers $S$ and a target sum $T$, determine
if there is a subset of $S$ whose elements sum to $T$.

Formal definition:
Let $D(i, j)$ be true if a sum of $j$ can be made using the first $i$ items.
$
D(i, j) = cases(
"true" & "if " j = 0,
"false" & "if " i = 0 " and " j > 0,
D(i-1, j) & "if " S_i > j,
D(i-1, j) or D(i-1, j - S_i) & "otherwise"
)
$

*As mapcode:*

_primitives_: `or`

$
I &= S:bb(N)^* times T:bb(N) quad quad quad "where" m = |S| \
X_(m,T) &= [0..m] times [0..T] -> {bot, "true", "false"} \
A &= {"true", "false"} \
rho(m,T) &= { (i,j) |-> bot | i in {0 dots m}, j in {0 dots T}} \
F_S(x_(i,j)) &= cases(
"true" & "if " j = 0,
"false" & "if " i = 0 " and " j > 0,
x_(i-1, j) & "if " S_(i-1) > j,
x_(i-1, j) or x_(i-1, j - S_(i-1)) & "otherwise"
) \
pi_(S,T) (x) &= x_(m,T)
$

#let inst_S = (3, 5, 8);
#let inst_T = 11;
#let inst_m = inst_S.len();

#figure(
caption: [Subset Sum DP table for $S = #inst_S$ and $T = #inst_T$.],
$#{
let rho = ((inst_m, inst_T)) => {
let x = ()
for i in range(0, inst_m + 1) {
let row = ()
for j in range(0, inst_T + 1) {
row.push(none)
}
x.push(row)
}
x
}
let F_i = (S) => (x) => ((i,j)) => {
if j == 0 {
true
} else if i == 0 and j > 0 {
false
} else {
let item = S.at(i - 1)
if item > j {
// Item is too large, 'without' case
let without = x.at(i - 1).at(j)
if without != none {
without
} else {
none
}
} else {
// 'with' or 'without'
let without = x.at(i - 1).at(j)
let with = x.at(i - 1).at(j - item)
if without != none and with != none {
without or with
} else {
none
}
}
}
}
let F = (S) => map_tensor(F_i(S), dim: 2)

let pi = ((S, T)) => (x) => {
let m = S.len()
x.at(m).at(T)
}

// draw DP table
let x_h(x, diff_mask:none) = {
set text(weight: "bold")
let rows = ()

// header row: Target Sum
let header_cells = ()
header_cells.push(rect(fill: green.transparentize(70%), inset: 4pt)[$i slash j$])
for j in range(0, inst_T + 1) {
header_cells.push(rect(fill: orange.transparentize(70%), inset: 4pt)[$#j$])
}
rows.push(grid(columns: header_cells.len() * (14pt,), rows: 14pt, align: center + horizon, ..header_cells))

for i in range(0, x.len()) {
let row = ()
// left label: Item from S
if i == 0 {
row.push(rect(fill: green.transparentize(70%), inset: 4pt)[$0$])
} else {
row.push(rect(fill: green.transparentize(70%), inset: 4pt)[$#inst_S.at(i - 1)$])
}

for j in range(0, x.at(i).len()) {
let val = if x.at(i).at(j) == true {
$checkmark$
} else if x.at(i).at(j) == false {
$times$
} else {
$bot$
}

if diff_mask != none and diff_mask.at(i).at(j) {
row.push(rect(stroke: gray, fill: yellow.transparentize(70%), inset: 4pt)[#val])
} else {
row.push(rect(stroke: gray, inset: 4pt)[#val])
}
}
rows.push(grid(columns: row.len() * (14pt,), rows: 14pt, align: center + horizon, ..row))
}
grid(align: center, ..rows)
}

mapcode-viz(
rho, F(inst_S), pi((inst_S, inst_T)),
X_h: x_h,
pi_name: [$pi ((#inst_m, #inst_T))$],
group-size: calc.min(3, inst_m),
cell-size: 55mm, scale-fig: 65%
)((inst_m, inst_T))
}$)
4 changes: 4 additions & 0 deletions main.typ
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e.
#include "algorithms/LongestCommonSubsequence.typ"
#pagebreak()
#include "algorithms/leetcode/P2_add-two-numbers.typ"
#pagebreak()
#include "algorithms/SubsetSum.typ"
#pagebreak()
#include "algorithms/RodCutting.typ"