diff --git a/algorithms/coin_change.typ b/algorithms/coin_change.typ new file mode 100644 index 0000000..ae53009 --- /dev/null +++ b/algorithms/coin_change.typ @@ -0,0 +1,93 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Coin Change (Minimum Coins) +#set math.equation(numbering: none) + +Compute the minimum number of coins needed to make a target amount. + +Formal definition: +$ +"CC"(0) &= 0\ +"CC"(n) &= min_(c in "coins", c <= n) (1 + "CC"(n-c)) quad "for" n > 0 +$ + +Example: coins = [1,3,4], amount = 6 +Minimum coins = 2 (using 3+3) + +*As mapcode:* + +_primitives_: `min` and `add`($+$) are strict. i.e min and add on $bot$ is undefined. + +$ +I = "amount": NN quad quad quad X &= [0.."amount"] -> NN_bot quad quad quad A = NN\ +rho("amount") &= {i -> bot | i in [0.."amount"], i > 0} union {0 -> 0}\ +F(x)[i] &= cases( + 0 & "if" i = 0, + min_(c in "coins", c <= i) {1 + x[i - c]} & "if" i > 0 "and deps defined", + bot & "otherwise" +)\ +pi(x) &= x["amount"] +$ + +#let coins = (1, 3, 4); +#let amount = 6; +#figure( + caption: [Coin Change computation using mapcode for amount = #amount, coins = #coins], +$ +#{ + let rho = (amount) => { + let x = () + for i in range(0, amount + 1) { + if i == 0 { + x.push(0) + } else { + x.push(none) + } + } + x + } + + let F_i = (x) => ((i,)) => { + if i == 0 { + 0 + } else { + let candidates = () + for c in coins { + if c <= i and x.at(i - c) != none { + candidates.push(x.at(i - c) + 1) + } + } + if candidates.len() > 0 { + calc.min(..candidates) + } else { + none + } + } + } + let F = map_tensor(F_i, dim: 1) + + let pi = (i) => (x) => x.at(i) + + 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) { + rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$] + } else { + rect(stroke: none, inset: 2pt)[$#val$] + } + }) + $vec(delim: "[", ..cells)$ + } + + mapcode-viz( + rho, F, pi(amount), + X_h: X_h, + pi_name: [$pi (#amount)$], + group-size: calc.min(7, amount + 1), + cell-size: 10mm, scale-fig: 85% + )(amount) +} +$ +) diff --git a/algorithms/lis.typ b/algorithms/lis.typ new file mode 100644 index 0000000..a207daa --- /dev/null +++ b/algorithms/lis.typ @@ -0,0 +1,101 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Longest Increasing Subsequence (LIS) +#set math.equation(numbering: none) + +Compute the length of the longest increasing subsequence in an array. + +Formal definition: +$ +"LIS"(i) = cases( + 1 & "if" i = 0, + 1 + max_(j < i, A[j] < A[i]) "LIS"(j) & "if" i > 0 "and valid j exists", + 1 & "otherwise" +) +$ + +Example: A = [10, 9, 2, 5, 3, 7, 101] +LIS length = 4 (subsequence: [2, 3, 7, 101] or [2, 5, 7, 101]) + +*As mapcode:* + +_primitives_: `max` and `add`($+$) are strict. i.e max and add on $bot$ is undefined. + +$ +I = (n: NN, A: "array") quad quad quad X &= [0..n-1] -> NN_bot quad quad quad A = NN\ +rho(n, A) &= {i -> 1 | i in [0..n-1]}\ +F(x)[i] &= cases( + 1 & "if" i = 0, + 1 + max_(j < i, A[j] < A[i]) {x[j]} & "if valid j and deps defined", + 1 & "otherwise" +)\ +pi(x) &= max_(i) x[i] +$ + +#let arr = (10, 9, 2, 5, 3, 7, 101); +#let n = arr.len(); +#figure( + caption: [LIS computation using mapcode for array = #arr], +$ +#{ + let rho = (n) => { + let x = () + for i in range(0, n) { + x.push(1) + } + x + } + + let F_i = (x) => ((i,)) => { + if i == 0 { + 1 + } else { + let max_len = 1 + for j in range(0, i) { + if arr.at(j) < arr.at(i) and x.at(j) != none { + max_len = calc.max(max_len, x.at(j) + 1) + } + } + max_len + } + } + let F = map_tensor(F_i, dim: 1) + + let pi = (n) => (x) => { + let max_val = 0 + for val in x { + if val != none { + max_val = calc.max(max_val, val) + } + } + max_val + } + + 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$]} + let arr_val = arr.at(i) + if diff_mask != none and diff_mask.at(i) { + rect(fill: yellow.transparentize(70%), inset: 2pt)[ + $#arr_val: #val$ + ] + } else { + rect(stroke: none, inset: 2pt)[ + $#arr_val: #val$ + ] + } + }) + $vec(delim: "[", ..cells)$ + } + + mapcode-viz( + rho, F, pi(n), + X_h: X_h, + pi_name: [$max_i x[i]$], + group-size: calc.min(7, n), + cell-size: 12mm, scale-fig: 80% + )(n) +} +$ +) diff --git a/main.pdf b/main.pdf new file mode 100644 index 0000000..c24eeb4 Binary files /dev/null and b/main.pdf differ diff --git a/main.typ b/main.typ index 9952d9b..ad742c1 100644 --- a/main.typ +++ b/main.typ @@ -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/lis.typ" +#pagebreak() +#include "algorithms/coin_change.typ"