diff --git a/.gitignore b/.gitignore index f444db2..9474994 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +algorithms/mergggee.typ +algorithms/evenodd.typ +algorithms/01-Knapsack.typ +algorithms/matrix_mul_chain.bak.typ # General .vscode .idea diff --git a/algorithms/MegeSort.typ b/algorithms/MegeSort.typ new file mode 100644 index 0000000..a3791d8 --- /dev/null +++ b/algorithms/MegeSort.typ @@ -0,0 +1,53 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Merge Sort + +Compute the binomial coefficient $C(n, k)$, which represents the number of ways to choose $k$ elements from a set of $n$ elements. + +Formal definition: +$ +m(a) := cases( + a & "if " |a| <= 1, + "merge"(m(h_l a), m(h_r a)) & "else" +) +$ + +Example: +- m(5, 2, 4, 2, 8) = (2, 2, 4, 5, 8) +- m(5, 0) = (0, 5) +- m(9, 6, 5, 3, 1, 8, 7, 2, 4) = (1, 2, 3, 4, 5, 6, 7, 8, 9) + +/* ddc */ +*As mapcode:* + +Primatives +1. array data structure. Also size of array $a$ is given by $|a|$ +2. merge function: $"merge"(a,b)$ returns sorted array containing elements of $a$ and $b$ +3. List Halving operators ($h_l$ and $h_r$): $h_l a$ returns an array with first $⌈(|a|)/2⌉$ elements of $a$ while $h_r a$ returns the rest +4. Given an array $a$, define family of sets $h^n a$ such that +$ +h^n a := cases( + a & "if " n=0, + h^{n-1} a union \{h_l x "|" x in h^{n-1} a\} union \{h_r x "|" x in h^{n-1} a\} & "else" +) +$ + + +$ +I = A = { "set of arrays" } quad +X_(a) & = h^{⌈log_2 |a|⌉}_a -> { "set of arrays" }_bot quad quad quad \ +rho(a) & = { "arr" -> bot | "arr" in h^⌈log_2 |a|⌉ a } \ +F(x("arr")) & = cases( + "arr" & "if " |"arr"|<=1, + "merge"(x(h_l "arr"),x(h_r "arr")) & "if " x(h_l "arr") "," x(h_r "arr") != bot, + bot & "else" + )\ +pi_(a) (x) & = x(a) +$ + + +#link("https://github.com/mayank3135432/recreational-programming-stuff/blob/main/merge_sort.ipynb")[ + Link to Python Implementation (See cell 11) +] + diff --git a/algorithms/OptimalMatrixChainProduct.typ b/algorithms/OptimalMatrixChainProduct.typ new file mode 100644 index 0000000..6be3ded --- /dev/null +++ b/algorithms/OptimalMatrixChainProduct.typ @@ -0,0 +1,161 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Optimal Matrix Chain Multiplication +#set math.equation(numbering: none) + +Given a sequence of matrices $A_(n_0 times n_1),A_(n_1 times n_2),..,A_(n_(q-1) times q_i)$, Compute the minimum number of scalar multiplication required to multiply all of them. +The algorithm takes in vector $d = (n_0,..,n_q)$ and returns an integer. + +Formal definition: $"num_mul"(d) := T_d (0,|d|-2)$ where + +$ + T_d (i, j) = min_{k|i<=k 38000 $ +- $"num_mul"(13, 5, 89, 3, 34) -> 2856$ + +*As mapcode:* + +_primitives_: $"min",+,<,<=$. \ +Non Comparitive Operations on $bot$ return $bot$. (For example: $4+bot=bot$ , $min({3,2,bot,10})=bot$) + +$ + k in NN \ + I = NN^k quad quad quad X & = NN times NN -> NN_bot quad quad quad + A = NN \ + rho(d) & = {(i,j) -> bot | i,j in NN} \ + F_d (x)(i,j) & = cases( + 0 & "if " i = j, + min { x(i,k)+ x(k+1,j)+d(i) dot d(j+1) dot d(k+1) |i<=k { + let x = () + for i in range(0, inst_dimvecs.len() - 1) { + let row = () + for w in range(0, inst_dimvecs.len() - 1) { + row.push(none) + } + x.push(row) + } + x + } + + let safe_operation(a, b, c, op) = { + if a == none or b == none or c == none { + none + } else { + op(a, b, c) + } + } + + let F_i(d) = (x) => ((i, j)) => { + if i == j { + 0 + } else { + let min_val = none + let mut = none + for k in range(i, j) { + let current = safe_operation( + x.at(i).at(k), + x.at(k+1).at(j), + d.at(i) * d.at(j+1) * d.at(k+1), + (a, b, c) => a + b + c + ) + + if current != none { + if min_val == none or current < min_val { + min_val = current + } + } else { + // If we want to propagate none immediately: + // return none + // If we want to check all k but return none if any is none: + min_val = none + } + } + min_val + } + } + + + let F = (inst_dimvecs) => map_tensor(F_i(inst_dimvecs), dim: 2) + + let pi = ((dimvec)) => (x) => { + let m = dimvec.len() + x.at(0).at(m - 2) + } + + // (2, 3, 4, 5), (3, 4, 5, 6), 5) + let I_h(inst_dimvecs) = { + [ + $d: vec(..#inst_dimvecs.map(i => [#i]), delim: "[")$ + ] + } + + + let x_h(x, diff_mask:none) = { + set text(weight: "bold") + let rows = () + + + let header_cells = () + header_cells.push(rect(fill: green.transparentize(70%), inset: 14pt)[$id$]) + + + for w in range(0, x.at(0).len()) { + header_cells.push(rect(fill: orange.transparentize(70%), inset: 14pt)[$#w$]) + } + rows.push(grid(columns: header_cells.len() * (30pt,), rows: 14pt, align: center + horizon, ..header_cells)) + + for i in range(0, x.len()) { + let row = () + + if i == 0 { + row.push(rect(fill: green.transparentize(70%), inset: 14pt)[$0$]) + }else{ + row.push(rect(fill: green.transparentize(70%), inset: 14pt)[$#i$]) + } + + for w in range(0, x.at(i).len()) { + let val = if x.at(i).at(w) != none {[$#x.at(i).at(w)$]} else {[$bot$]} + if diff_mask != none and diff_mask.at(i).at(w) { + row.push(rect(stroke: gray, fill: yellow.transparentize(70%), inset: 14pt)[$#val$]) + } else { + row.push(rect(stroke: gray, inset: 14pt)[$#val$]) + } + } + + rows.push(grid(columns: row.len() * (30pt,), rows: 14pt, align: center + horizon, ..row)) + } + + grid(align: center, ..rows) + } + + mapcode-viz( + rho, F(inst_dimvecs), pi(inst_dimvecs), + I_h: I_h, + X_h: x_h, + F_name: [$F_(w,v)$], + pi_name: [$\pi_(w,v,C)$], + group-size: calc.min(3, inst_dimvecs.len()), + cell-size: 60mm, scale-fig: 75% + )(inst_dimvecs) +}$) + diff --git a/main.typ b/main.typ index 9952d9b..23173cc 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/OptimalMatrixChainProduct.typ" +#pagebreak() +#include "algorithms/MegeSort.typ" \ No newline at end of file