Skip to content

Commit

Permalink
chore: tweak appearance
Browse files Browse the repository at this point in the history
  • Loading branch information
tiankaima committed May 31, 2024
1 parent 9cd1aa0 commit 61c8f84
Show file tree
Hide file tree
Showing 26 changed files with 913 additions and 1,243 deletions.
66 changes: 34 additions & 32 deletions 7e1810-algo_hw/hw1.typ
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@

== HW 1 (Week 2)
Due: 2024.03.17

#let ans(it) = [
#pad(1em)[
#text(fill: blue)[
#it
]
]
]

=== Question 2.3-5

You can also think of insertion sort as a recursive algorithm. In order to sort $A[1 : n]$, recursively sort the subarray $A[1 : n – 1]$ and then insert $A[n]$ into the sorted subarray $A[1 : n – 1]$. Write pseudocode for this recursive version of insertion sort. Give a recurrence for its worst-case running time.

#text(fill: blue)[
=== Solution 2.3-5

#ans[
The pseudocode for this recursive version of insertion sort is as follows:

```txt
Expand Down Expand Up @@ -38,40 +44,36 @@ You can also think of insertion sort as a recursive algorithm. In order to sort

Although merge sort runs in $Theta(n lg n)$ worst-case time and insertion sort runs in $Theta(n^2)$ worst-case time, the constant factors in insertion sort can make it faster in practice for small problem sizes on many machines. Thus it makes sense to coarsen the leaves of the recursion by using insertion sort within merge sort when subproblems become suffificiently small. Consider a modifification to merge sort in which $n\/k$ sublists of length $k$ are sorted using insertion sort and then merged using the standard merging mechanism, where $k$ is a value to be determined.

- a. Show that insertion sort can sort the $n\/k$ sublists, each of length $k$, in $Theta(n k)$ worst-case time.
- b. Show how to merge the sublists in $Theta(n lg(n\/k))$ worst-case time.
- c. Given that the modifified algorithm runs in $Theta(n k + n lg(n\/k))$ worst-case time, what is the largest value of $k$ as a function of $n$ for which the modifified algorithm has the same running time as standard merge sort, in terms of $Theta$-notation?
- d. How should you choose $k$ in practice?

#text(fill: blue)[
=== Solution 2-1

- a. For each sublist, the insertion sort can sort the $k$ elements in $Theta(k^2)$ worst-case time. Thus, the insertion sort can sort the $n\/k$ sublists, each of length $k$, in $Theta(n k)$ worst-case time.
- b. Given $n\/k$ sorted sublists, each of length $k$, the recurrence for merging the sublists is
$
T(n) = cases(2 dot.c T(n\/2) + Theta(n) space.quad & n>k, 0 & n=k)
$
The solution to the recurrence is $Theta(n lg(n\/k))$ worst-case time.

*This could also be viewed as a tree with $lg(n\/k)$ levels with $n$ element in each level. Worst case would be $Theta(n lg (n\/k))$*

- c. Take $Theta(n k + n lg(n \/ k)) = Theta(n lg n)$, consider $k = Theta(lg n)$:
$
Theta(n k + n lg(n \/ k))
&= Theta (n k + n lg n - n lg k) \
&= Theta (n lg n + n lg n - n lg (lg n)) \
&= Theta (n lg n)
$
- d. Choose $k$ to be the largest length of sublist for which insertion sort is faster than merge sort. Use a small constant such as $5$ or $10$.
+ Show that insertion sort can sort the $n\/k$ sublists, each of length $k$, in $Theta(n k)$ worst-case time.
+ Show how to merge the sublists in $Theta(n lg(n\/k))$ worst-case time.
+ Given that the modifified algorithm runs in $Theta(n k + n lg(n\/k))$ worst-case time, what is the largest value of $k$ as a function of $n$ for which the modifified algorithm has the same running time as standard merge sort, in terms of $Theta$-notation?
+ How should you choose $k$ in practice?

#ans[
+ For each sublist, the insertion sort can sort the $k$ elements in $Theta(k^2)$ worst-case time. Thus, the insertion sort can sort the $n\/k$ sublists, each of length $k$, in $Theta(n k)$ worst-case time.
+ Given $n\/k$ sorted sublists, each of length $k$, the recurrence for merging the sublists is
$
T(n) = cases(2 dot.c T(n\/2) + Theta(n) space.quad & n>k, 0 & n=k)
$
The solution to the recurrence is $Theta(n lg(n\/k))$ worst-case time.

*This could also be viewed as a tree with $lg(n\/k)$ levels with $n$ element in each level. Worst case would be $Theta(n lg (n\/k))$*

+ Take $Theta(n k + n lg(n \/ k)) = Theta(n lg n)$, consider $k = Theta(lg n)$:
$
Theta(n k + n lg(n \/ k))
&= Theta (n k + n lg n - n lg k) \
&= Theta (n lg n + n lg n - n lg (lg n)) \
&= Theta (n lg n)
$
+ Choose $k$ to be the largest length of sublist for which insertion sort is faster than merge sort. Use a small constant such as $5$ or $10$.

]

=== Question 4.2-3
What is the largest $k$ such that if you can multiply $3 times 3$ matrices using $k$ multiplications (not assuming commutativity of multiplication), then you can multiply $n times n$ matrices in $o(n lg 7)$ time? What is the running time of this algorithm?

#text(fill: blue)[
==== Solution 4.2-3

#ans[
Assuming $n = 3^m$. Use block matrix multiplication, the recursive running time is $T(n) = k T(n\/3) + O(1)$.

When $log_3 k > 2 $, using master theorem, the largest $k$ to satisfy $log_3 k < lg 7$ is $k=21$.
Expand Down
83 changes: 37 additions & 46 deletions 7e1810-algo_hw/hw2.typ
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
== HW 2 (Week 3)
Due: 2024.03.24
=== Question 6.2-6
The code for MAX-HEAPIFY is quite efficient in terms of constant factors, except possibly for the recursive call in line 10, for which some compilers might produce inefficient code. Write an efficient MAX-HEAPIFY that uses an iterative control construct (a loop) instead of recursion.

#text(fill: blue)[
==== Solution 6.2-6
#let ans(it) = [
#pad(1em)[
#text(fill: blue)[
#it
]
]
]

=== Question 6.2-6
The code for MAX-HEAPIFY is quite efficient in terms of constant factors, except possibly for the recursive call in line 10, for which some compilers might produce inefficient code. Write an efficient MAX-HEAPIFY that uses an iterative control construct (a loop) instead of recursion.
#ans[
Consider the following pseudocode code:
```txt
MAX-HEAPIFY(A, i)
Expand All @@ -30,57 +36,49 @@ The code for MAX-HEAPIFY is quite efficient in terms of constant factors, except
=== Question 6.5-9
Show how to implement a first-in, first-out queue with a priority queue. Show how to implement a stack with a priority queue. (Queues and stacks are defined in Section 10.1.3.)

#text(fill: blue)[
==== Solution 6.5-9

#ans[
- For stack, add element with increasing priority, and pop the element with the highest priority, pseudocode:
// ```txt
// PUSH(S, x)
// S.top = S.top + 1
// S[S.top] = x
// POP(S)
// if S.top < 1
// error "underflow"
// else
// S.top = S.top - 1
// return S[S.top + 1]
// ```
```txt
PUSH(S, x)
S.top = S.top + 1
S[S.top] = x
POP(S)
if S.top < 1
error "underflow"
else
S.top = S.top - 1
return S[S.top + 1]
```
- For queue, add element with decreasing priority, and pop the element with the highest priority, pseudocode:
// ```txt
// ENQUEUE(Q, x)
// Q.tail = Q.tail + 1
// Q[Q.tail] = x
// DEQUEUE(Q)
// if Q.head > Q.tail
// error "underflow"
// else
// return Q[Q.head]
// ```
```txt
ENQUEUE(Q, x)
Q.tail = Q.tail + 1
Q[Q.tail] = x
DEQUEUE(Q)
if Q.head > Q.tail
error "underflow"
else
return Q[Q.head]
```

]

=== Question 7.4-6
Consider modifying the PARTITION procedure by randomly picking three elements from subarray $A[p : r]$ and partitioning about their median (the middle value of the three elements). Approximate the probability of getting worse than an $alpha$-to-$(1 - alpha)$ split, as a function of $alpha$ in the range $0 < alpha < 1/2$.

#text(fill: blue)[
==== Solution 7.4-6

#ans[
*Assuming the same element could be picked more than once*(which should be the case in real world).

The probability of getting worse than an $alpha$-to-$(1 - alpha)$ split is the probability of picking the smallest or the largest element as the median.

$
P = 2 * [binom(2,3) times alpha^2(1 - alpha) + alpha^3] = 6 alpha^2 - 4 alpha^3
P = 2 * [binom(2,3) times alpha^2(1 - alpha) + alpha^3] = 6 alpha^2 - 4 alpha^3
$

]

=== Question 8.2-7
Counting sort can also work efficiently if the input values have fractional parts, but the number of digits in the fractional part is small. Suppose that you are given n numbers in the range $0$ to $k$, each with at most $d$ decimal (base $10$) digits to the right of the decimal point. Modify counting sort to run in $Theta(n + 10^d k)$ time.

#text(fill: blue)[
==== Solution 8.2-7

#ans[
To achieve $Theta(n + 10^d k)$ time, we first use $Theta(n)$ time to multiply each number by $10^d$, then change the $C[0, k]$ to $C[0, 10^d k]$, and finally use $Theta(10^d k)$ time to sort the numbers.

With other part of the counting sort unchanged, the pseudocode is as follows:
Expand All @@ -104,10 +102,7 @@ Counting sort can also work efficiently if the input values have fractional part

=== Question 8.3-5
Show how to sort $n$ integers in the range $0$ to $n^3 - 1$ in $O(n)$ time.

#text(fill: blue)[
==== Solution 8.3-5

#ans[
First convert each number to base $n$, then use counting sort to sort the numbers.

Since each number would now have at most $log_n n^3 = 3$ digits, 3 passes of counting sort would be enough to sort the numbers, during which each pass would take $O(n)$ time since there's only $n$ numbers.
Expand All @@ -116,15 +111,11 @@ Show how to sort $n$ integers in the range $0$ to $n^3 - 1$ in $O(n)$ time.

=== Question 9.3.9
Describe an $O(n)$-time algorithm that, given a set $S$ of $n$ distinct numbers and a positive integer $k <= n$, determines the $k$ numbers in $S$ that are closest to the median of $S$.

#text(fill: blue)[
==== Solution 9.3.9

#ans[
+ $O(n)$: Using SELECT, we can find $x$ to be the median of $S$.
+ $O(n)$: Subtract $x$ from each element in $S$.
+ $O(n)$: Use COUNTING-SORT to sort the absolute values of the differences.
+ $O(k)$: Return the first $k$ elements in the sorted array.

This is the required $O(n)$-time algorithm.

]
24 changes: 12 additions & 12 deletions 7e1810-algo_hw/hw3.typ
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
== HW 3 (Week 4)
Due: 2024.03.31

#let ans(it) = [
#pad(1em)[
#text(fill: blue)[
#it
]
]
]

=== Question 12.2-3
Write the `TREE-PREDECESSOR` procedure(which is symmetric to `TREE-SUCCESSOR`).

#text(fill: blue)[
=== Solution 12.2-3

#ans[
```txt
TREE-PREDECESSOR(x)
if x.left != nil
Expand All @@ -22,9 +28,7 @@ Write the `TREE-PREDECESSOR` procedure(which is symmetric to `TREE-SUCCESSOR`).
=== Question 13.1-5
Show that the longest simple path from a node $x$ in red-black tree to a descendant leaf at most twice that of the shortest simple path from node $x$ to a descendant leaf.

#text(fill: blue)[
=== Solution 13.1-5

#ans[
Consider the longest simple path $(a_1, ... ,a_s)$ & the shortest simple path $(b_1, ... ,b_t)$, they have equal number of black nodes (Property 5).
Neither of the paths can have repeated red node (Property 4).
Thus at most $floor((s - 1) / 2)$ of the nodes in the longest path are red, so $ t >= ceil((s+1)/2) $ If by way of contradiction, we had $s > t dot 2$, then $ t >= ceil((s+1) / 2) >= ceil(t+1) = t+1 $ which is a contradiction.
Expand All @@ -33,17 +37,13 @@ Show that the longest simple path from a node $x$ in red-black tree to a descend
=== Question 17.1-7
Show how to use an order-statistic tree to count the number of inversions in an array of $n$ distinct elements in $O(n lg n)$ time.

#text(fill: blue)[
=== Solution 17.1-7

#ans[
$O(n lg(n))$ time is required to build a red-black treem so everytime we insert a node, we can calculate the number of inversion using $"OS-RANK"$ (which is the rank of the node, thus calculating inversions).
]

=== Question 17.3-2
Describe an efficient algorithm that, given an interval $i$, returns an interval overlapping $i$ that has the minimum low endpoint, or $T."nil"$ if no such interval exists.

#text(fill: blue)[
=== Solution 17.3-2

#ans[
Modify the usual interval search not to break out the loop when a overlap is found, but to keep track of the minimum low endpoint. Return the interval with the minimum low endpoint if found, otherwise return $T."nil"$.
]
17 changes: 11 additions & 6 deletions 7e1810-algo_hw/hw4.typ
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
== HW4 (Week 5)
Due: 2024.04.07

#let ans(it) = [
#pad(1em)[
#text(fill: blue)[
#it
]
]
]

=== Question 14.4-2
Give pseudocode to reconstruct an LCS from te completed c table (See Theorem 14.1 Optimal substructure of an LCS) and the original sequences $X = angle.l x_1, x_2, dots.c, x_m angle.r$ and $Y = angle.l y_1, y_2, dots.c y_n angle.r$ in $O(m+n)$ time, without using the b table.

#text(fill: blue)[
=== Solution 14.4-2

#ans[
Consider the following pseudocode:

```txt
Expand All @@ -25,9 +32,7 @@ Give pseudocode to reconstruct an LCS from te completed c table (See Theorem 14.
=== Question 14.4-5
Give an $O(n^2)$-time algorithm to find the longest monotonically increasing subsequence of a sequence of $n$ numbers.

#text(fill: blue)[
=== Solution 14.4-5

#ans[
Given a sequence of numbers $L$, make a copy and sort it, let the $L^'$ be the sorted array:
$o(n^2)$ time to sort $L$

Expand Down
21 changes: 13 additions & 8 deletions 7e1810-algo_hw/hw5.typ
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
#import "@preview/cetz:0.2.2": canvas, draw, tree
#import "@preview/cetz:0.2.2": *

== HW5 (Week 6)
Due: 2024.04.14

#let ans(it) = [
#pad(1em)[
#text(fill: blue)[
#it
]
]
]

=== Question 14.5-2
Determine the cost and structure of an optimal binary serach tree for a set of $n=7$ keys with the following probabilities:

Expand Down Expand Up @@ -44,9 +52,7 @@ Determine the cost and structure of an optimal binary serach tree for a set of $
)
]

#text(fill: blue)[
=== Solution 14.5-2

#ans[
Running the code provided in appendix, we get the following result (cost, preorder traversal of the optimal BST):
```text
(3.17, [5, 2, 1, 3, 4, 7, 6])
Expand Down Expand Up @@ -127,8 +133,7 @@ What is an optimal Huffman code for the following set of frequencies, based on t

Can you generalize your answer to find the optimal code when the frequencies are the first $n$ Fibonacci numbers?

#text(fill: blue)[
=== Solution 15.3-3
#ans[
#align(center)[
#table(
stroke: none,
Expand Down Expand Up @@ -175,8 +180,8 @@ Can you generalize your answer to find the optimal code when the frequencies are
Proof is also trivial, let's discuss sums of Fibonacci first:

$
f_n = f_(n-1) + f_(n-2) => f_(n) = f_(n+2) - f_(n-1)\
sum_(i=0)^n f_i = f_(n+2) - 1 => sum_(i=0)^n f_i < f_(n+2)
f_n = f_(n-1) + f_(n-2) => f_(n) = f_(n+2) - f_(n-1)\
sum_(i=0)^n f_i = f_(n+2) - 1 => sum_(i=0)^n f_i < f_(n+2)
$

so after merging the first $k$ elements, we're left with $((sum_(i=0)^k f_i), f_(k+1), dots.c, f_n)$, amoung which $((sum_(i=k)^n f_i), f_(k+1))$ are the smallest two, so they should be merged first, and so on, using induction it's easy to prove Huffman generates such tree, thus giving the optimal code.
Expand Down
Loading

0 comments on commit 61c8f84

Please sign in to comment.