## Exercises - Analysis of Algorithms

1. Give the Tilde approximations for the following functions:

1. $1/n + 1$
2. $7n^8 + 3n - 4$
3. $5n + 4n \log n$

1. $1$
2. $7n^8$
3. $4n \log n$

2. Prove that $C(n) = 3n^3 + 2n + 7$ is $\Theta(n^3)$. You may assume $n \ge 1$.

Given that $n \ge 1$, it must be the case that $C(n) = 3n^3 + 2n + 7 \le 3n^3 + 2n^3 + 7n^3 = 12n^3$. Further, $C(n) = 3n^3 + 2n + 7 \ge n^3$ for all positive $n$. Hence $|n^3| \le C(n) \le 12|n^3|$, so $C(n)$ is $\Theta(n^3)$.

3. Show that the function $C(n) = n^3 + 20n + 1$ is not $O(n^2)$.

We argue indirectly. Suppose $C(n)$ is $O(n^2)$. Then there exists some constant $k$ such that for sufficiently large $n$ we always have $C(n) \le k \cdot n^2$. This means that $n^3 + 20n + 1 \le k \cdot n^2$. However, dividing both sides by $n^2$ quickly shows this is impossible as it gives $$n + \frac{20}{n} + \frac{1}{n^2} \le k$$ To see this, note that as $n \rightarrow \infty$ left side grows without bound, while the right side is constant. Having reached a contradiction, we have shown that $C(n)$ can not possibly be $O(n^2)$.

4. Show that the function $C(n) = n^3 + 20n$ is $\Omega(n^2)$.

Recall, $C(n)$ is $\Omega(n^2)$ if there is some constant $k$ such that $C(n) \ge k \cdot n^2$ for sufficiently large $n$. Disregarding $n=0$, note $n^3 + 20n \ge k \cdot n^2$ and $n + 20/n \ge k$ are equivalent. Further, the latter inequality always holds for a sufficiently large $n$ as the left side of the inequality grows without bounds as $n$ increases (for sufficiently large $n$), while the right side is constant.

5. Find the cost function (associated with time complexity) for the indicated primitive operation of interest.

function(int n) {
if (n==1)
return;
for (int i=1; i<=n; i++) {
for (int j=1; j<=n; j++)
{
print("*"); //<-- primitive operation of interest
break;
}
}
}


Note that even though the inner loop continuation condition is j<=n, its body executes only once due to the break statement. Hence, $C(n) = n$

6. Find the cost function (associated with time complexity) for the indicated primitive operation(s) of interest, and the corresponding tilde approximation. Assume $n$ is even.

int count = 0;
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
for (int j = 0; j < n; j++)
count++;  // <-- primitive operation of interest
}
else {
for (int j = 0; j < 10; j++)
count++;  // <-- primitive operation of interest
}
}


$\displaystyle{C(n) = \frac{n^2}{2} + 5n \sim \frac{n^2}{2}}$ (for even $n$), and for the curious...

$\displaystyle{C(n) = \frac{n^2 + 11n - 10}{2} \sim \frac{n^2}{2}}$ (for odd $n$)

7. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation.

for (int i = 0; i < n; i++) {
for (int j = n; j > i+1; j--) {
System.out.println("plus");     // primitive operation
System.out.println("minus");     // primitive operation
}
}


$C(n) = n^2 - n \sim n^2$

8. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation. Assume $n$ is even.

int count = 0;
for (int i = 0; i < n; i++) {
for (int j = i; j < 2*n; j++) {
count++;  // <-- primitive operation of interest
}
}


$\displaystyle{C(n) = \frac{3n^2}{2} + \frac{n}{2} \sim \frac{3n^2}{2}}$

9. Find the cost function (associated with time complexity) for the indicated primitive operations of interest, and the corresponding tilde approximation. Assume $n$ is even.

for (int i = 0; i < n; i++) {
for (int j = i+1; j < n/2; j++) {
System.out.println("fee");     // primitive operation of interest
System.out.println("fi");     // primitive operation of interest
}
}


$\displaystyle{C(n) = \frac{n^2}{4} - \frac{n}{2} \sim \frac{n^2}{4}}$

10. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation. Assume $n = 2^k$ for some positive integer $k$.

for (int i = n/2; i < n; i *= 2) {
System.out.println("fo fum");     // primitive operation of interest
}


$\displaystyle{C(n) = 1 \sim 1}$

11. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation. Assume $n=2^k$ for some positive integer $k$.

for (int i = 1; i < n/4; i *= 2) {
System.out.println("stop short");     // primitive operation
}
for (int i = 0; i < 2*n; i += 2) {
System.out.println("more stuff");     // primitive operation
}


$n + \log_2 n - 2$

12. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation.

for (int i = 0; i < n; i++)
for (int j = i+1; j < n; j++)
for (int k = j+1; k < n; k++)
count++;  // primitive operation of interest


$\displaystyle{C(n) = \frac{n(n-1)(n-2)}{6} \sim \frac{n^3}{6}}$.

Interestingly, you can express this with combinations (i.e., $C(n) = {}_n C_3$) as each combination of $3$ values taken from the group of $n$ values between $0$ and $(n-1)$ inclusive, is encountered once (where $i$, $j$, and $k$ are these values in ascending order, respectively.

In case you are rusty with combinations, remember that ${}_n C_k$ counts the number of groups of $k$ items you can choose from a group of $n$ items. (order doesn't matter -- just who is in the group). The formula for finding this count is given by $${}_n C_k = \frac{n!}{k! (n-k)!}$$

13. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation. Assume that $n = 3^k$ for some positive integer $k$

for (int i = 1; i < n; i++) {
count++;     // primitive operation of interest
}
for (int j = 1; j < n; j *= 3) {
count++;     // primitive operation of interest
}


$\displaystyle{C(n) = n + \log_3 n - 1 \sim n}$.

14. Find the cost function (associated with time complexity) for the indicated primitive operation of interest. For simplicity, assume $n$ is a positive integer power of 2.

int count = 0;
for (int i = n/2; i < n; i++)
for (int j = 1; j < n; j = 2*j)
for (int k = 1; k < n; k *= 2)
count++;  // <-- primitive operation of interest


Note that the innermost and second innermost loops both execute $\sim \log_2 n$ times, while the outer loop executes $n/2$ times. Thus, the cost function here is $C(n) \sim (n/2) \log_2^2 n$.

15. Find the cost function (associated with time complexity) for the indicated primitive operation of interest, and the corresponding tilde approximation. For simplicity, assume $n$ is a positive integer power of 2.

int count = 0;
for (int i = n/2; i <= n; i++)
for (int j = 1; j + n/2 <= n; j++)
for (int k = 1; k <= n; k = k*2)
count++; // <-- primitive operation of interest


The inner-most loop executes $(\log_2 n + 1)$ times, while the middle loop executes $n/2$ times, and the outer loop executes $(n/2 + 1)$ times. Thus,

$$C(n) = \left( \frac{n}{2} + 1 \right) \left( \frac{n}{2} \right)(\log_2 n + 1) \sim \frac{n^2 \log_2 n}{4}$$
16. Use Tilde notation to express the running time associated with the indicated primitive operation of interest. (For the ambitious -- find a formula for the exact value of the cost function for any integer $n$.)

int i = 1;
int s = 1;
while (s <= n) {
i++;
s += i;
print("*"); // <-- primtive operation of interest
}


Note that the primitive operation of interest happens as many times as $s$ is increased. Suppose this happens $C(n)=k$ times.

Then, we can relate $n$ and $k$ together by considering the value of $s$: $$s = 1 + 2 + 3 + \cdots + k \le n < 1 + 2 + 3 + \cdots + k + (k+1)$$

Collapsing the sums on either side, we have: $$\frac{k(k+1)}{2} \le n < \frac{(k+1)(k+2)}{2}$$ We want $k$ in terms of $n$, and so let us eliminate the denominators by multiplying by $2$: $$k(k+1) \le 2n < (k+1)(k+2)$$ At this point we can argue that $C(n) \sim \sqrt{2n}$, given that both $k(k+1)$ and $(k+1)(k+2)$are $\sim k^2$, provided $C(n)=k$ is a function that increases without bound -- which it does here.

Interestingly in this situation, one can predict the exact value of the cost function for any $n$. To see this, note that we can solve $k(k+1) <= 2n$ for $k$ (which must be a positive integer) to obtain $$k <= \frac{-1 + \sqrt{1+8n}}{2}$$ Similarly, we can solve $(k+1)(k+2) > 2n$ to find $$k > \frac{-3 + \sqrt{1+8n}}{2}$$ Thus, $$\frac{-3 + \sqrt{1+8n}}{2} < C(n) \le \frac{-1 + \sqrt{1+8n}}{2}$$ (We pause here to note that one could use the squeeze theorem from calculus to establish $\lim_{n \rightarrow \infty} C(n) = \sqrt{2n}$, which further validates that $C(n) \sim \sqrt{2n}$.)

Finally, note that the interval of possible values for $C(n)$ described by this last string inequality is exactly one unit wide. As $C(n)$ must be an integer (as it counts the frequency of execution for our primitive operation), $C(n)$ must be the unique integer contained in this interval.

Appealing to the floor function, we can then write a formula for $C(n)$: $$C(n) = \left \lfloor \frac{-1 + \sqrt{1+8n}}{2} \right \rfloor$$

17. In each table below, the execution times of a related snippet of code is found for various values of $n$ (the "problem size"), these times are given by the value of $C(n)$ in the corresponding row of the table below. For convenience, the value of $C(2n)/C(n)$ is also given for all but the last row. Assuming $C(n) \sim k n^p$ for some constant $k$ and integer $p$, what does $p$ appear to be in each case?

1. $\displaystyle{\begin{array}[t]{|c|c|c|}\hline n & C(n) & C(2n)/C(n)\\\hline 4 & 10 & 2.1\\\hline 8 & 21 & 5.57\\\hline 16 & 117 & 7.31\\\hline 32 & 855 & 7.70\\\hline 64 & 6584 & 7.92\\\hline 128 & 52144 & 7.98\\\hline 256 & 416112 & \vdots \\\hline \end{array}}$

2. $\displaystyle{\begin{array}[t]{|c|c|c|}\hline n & C(n) & C(2n)/C(n)\\\hline 4 & 10 & 2.2\\\hline 8 & 22 & 8.56\\\hline 16 & 188 & 11.29\\\hline 32 & 2126 & 14.70\\\hline 64 & 31254 & 15.59\\\hline 128 & 487252 & 15.97\\\hline 256 & 7781418 & \textrm{etc.} \\\hline \end{array}}$

Using "doubling time" analysis, we expect for large $n$ and a cost function of $C(n) \sim k n^p$ that $C(2n)/C(n)$ approach $2^p$. In part (a), this fraction appears to approach $2^3 = 8$ in the table, suggesting that $p = 3$. In part (b), this fraction appears to approach $2^4 = 16$, suggesting $p = 4$.