Assignment 1#

Dave Friedman
CMPSC 465 Data Structures and Algorithms
Summer 2024


Table of Contents#


Auxiliary Code#

Hide code cell source
import numpy             as np
from   scipy.special     import factorial
import matplotlib        as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot');
plt.rcParams.update({'text.usetex' : True});
%matplotlib inline

Problem 1 - Big Oh#


1A#

\( \begin{aligned} f(n) &= n^{10000} \\ g(n) &= 2^n \\ \end {aligned} \)

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{n^{10000}}{2^n} \end {aligned} \)

One application of L’Hospital’s rule yields:

\( \begin{aligned} \lim_{n \to \infty} \frac{10,000 \cdot n^{9999}}{\ln 2 \cdot 2^n} = \frac{10,000}{\ln 2} \lim_{n \to \infty} \frac{n^{9999}}{2^n} \end {aligned} \)

Ten thousand applications of L’Hospital’s rule yields:

\( \begin{aligned} \left( \frac{10,000}{\ln 2} \times \frac{9,999}{\ln 2} \times \dots \times \frac{2}{\ln 2} \times \frac{1}{\ln 2} \right) \lim_{n \to \infty} \frac{1}{2^n} = \left( \frac{10,000!}{\ln^{10,000} 2} \right) \lim_{n \to \infty} \frac{1}{2^n} \end {aligned} \)

The factor \(\begin{aligned} \frac{10,000!}{\ln^{10,000} 2} \end{aligned}\) is just a constant and \(\begin{aligned} \lim_{n \to \infty} \frac{1}{2^n} \end{aligned} = 0\) and so

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{n^{10000}}{2^n} = 0 \end {aligned} \)

Therefore

\(f = o(g) \implies f = O(g)\)

where the antecedent states that function \(f\) grows slower than a constant multiple of function \(g\) for all sufficiently large \(n\)

and the consequent states that function \(f\) grows no faster than a constant multiple of function \(g\) for all sufficiently large \(n\)

Hide code cell source
s = 10
n = np.linspace(0, s, 1001)
f = n**10 # n = 10,000
g = 2**n

fig = plt.figure();
ax  = plt.subplot();
ax.set_aspect(1);
ax.plot(n, f, label='$n^{10,000}$', linewidth=2, color='#6495ED');
ax.plot(n, g, label='$2^n$',        linewidth=2, color='#ED7864');
ax.set_facecolor('#c0c6d1')
ax.set_xlim(0,s);
ax.set_ylim(0,s);
ax.legend();
../../../../_images/a6fa6fe063fa5b2bc9f279b057d221ffed103326b4eb065a4fca762ecc5df6aa.png

1B#

\( \begin{aligned} f(n) &= n \\ g(n) &= \sqrt{n} \\ \end {aligned} \)

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{n}{\sqrt{n}} = \lim_{n \to \infty} (n^{\frac{1}{2}}) = \left( \lim_{n \to \infty} n \right)^{\frac{1}{2}} = \infty \end {aligned} \)

Therefore

\(f = \omega(g) \implies f = \Omega(g)\)

where the antecedent states that function \(f\) grows faster than a constant multiple of function \(g\) for all sufficiently large \(n\)

and the consequent states that function \(f\) grows no slower than a constant multiple of function \(g\) for all sufficiently large \(n\)

Hide code cell source
s = 10
n = np.linspace(0, s, 1001)
f = n
g = n**0.5

fig = plt.figure();
ax  = plt.subplot();
ax.set_aspect(1);
ax.plot(n, f, label='$n$',        linewidth=2, color='#6495ED');
ax.plot(n, g, label='$\sqrt{n}$', linewidth=2, color='#ED7864');
ax.set_facecolor('#c0c6d1')
ax.set_xlim(0,s);
ax.set_ylim(0,s);
ax.legend();
../../../../_images/223f728f805c62c34163ea1451b9444fb546dd60e91c2b084131f06a9ae8e488.png

1C#

\( \begin{aligned} f(n) &= 5n + \sqrt{n} \\ g(n) &= 3n + \log n \\ \end {aligned} \)

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{5n + \sqrt{n}}{3n + \log n} = \lim_{n \to \infty} \frac{5 + \frac{1}{n^{\frac{1}{2}}}}{3 + \frac{\log n}{n}} = \frac{5 + \lim_{n \to \infty} \frac{1}{n^{\frac{1}{2}}}}{3 + \lim_{n \to \infty} \frac{\log n}{n}} \end {aligned} \)

\( \begin{aligned} \lim_{n \to \infty} \frac{\log n}{n} \overset{\text{L'Hospital}}{=} \lim_{n \to \infty} \frac{\frac{1}{n}}{1} = \lim_{n \to \infty} \frac{1}{n} = 0 \end {aligned} \)

\( \begin{aligned} \frac{5 + \lim_{n \to \infty} \frac{1}{n^{\frac{1}{2}}}}{3 + \lim_{n \to \infty} \frac{\log n}{n}} = \frac{5}{3} \end {aligned} \)

Therefore

\(f = \Theta(g)\)

function \(f\) grows at the same rate as a constant multiple of function \(g\) for all sufficiently large \(n\)

Hide code cell source
s = 10
n = np.linspace(0.1, s, 1001)
f = 5*n + np.sqrt(n)
g = 3*n + np.log(n)

fig = plt.figure();
ax  = plt.subplot();
ax.set_aspect(1);
ax.plot(n, f, label='', linewidth=2, color='#6495ED');
ax.plot(n, g, label='', linewidth=2, color='#ED7864');
ax.set_facecolor('#c0c6d1')
ax.set_xlim(0,s);
ax.set_ylim(0,s);
../../../../_images/b60ffac9010bdf3fc7461869b8aa3e229d201daba0b68d830d7c51c222c01c4f.png

1D#

\( \begin{aligned} f(n) &= \log (n!) \\ g(n) &= n \log n \\ \end {aligned} \)

\( \begin{aligned} \log (n!) &= \log (n \times n - 1 \times \dots \times 2 \times 1) &&= \log (n) && + && \log (n - 1) && + && \dots && + && \log (2) && + && \log (1) \\ n \log n & &&= \log (n) && + && \log (n) && + && \dots && + && \log (n) && + && \log (n) \\ \end {aligned} \)

Therefore the limit has an upper bound of \(1\):

\(\log (n!) \le n \log n \implies \frac{\log (n!)}{n \log n} \le 1\)

How to show a lower bound of \(1\)?

https://math.stackexchange.com/questions/376988/limit-of-frac-lognn-logn-as-n-to-infty

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{\log (n!)}{n \log n} = 1 \end {aligned} \)

Therefore

\(f \sim g \implies f = \Theta(g)\)

not only does function \(f\) grow at the same rate as a constant multiple of function \(g\) for all sufficiently large \(n\) but \(\frac{f}{g}\) approaches \(1\)

Hide code cell source
s = 10
n = np.linspace(0.1, s, 1001)
f = np.log(factorial(n))
g = n * np.log(n)

fig = plt.figure();
ax  = plt.subplot();
ax.set_aspect(1);
ax.plot(n, f, label='', linewidth=2, color='#6495ED');
ax.plot(n, g, label='', linewidth=2, color='#ED7864');
ax.set_facecolor('#c0c6d1')
ax.set_xlim(0,s);
ax.set_ylim(0,s);
../../../../_images/766648368d1a53a11265a55cfa28e888c943781b666e896dea4fd1ac63f02dec.png

1E#

\( \begin{aligned} f(n) &= \log^{100} n \\ g(n) &= n^{.001} \\ \end {aligned} \)

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{\log^{100} n}{n^{.001}} \end {aligned} \)

One application of L’Hospital’s rule yields:

\( \begin{aligned} \lim_{n \to \infty} \frac{100 \frac{1}{n} \cdot \log^{99} n}{\frac{1}{1000} \cdot \frac{1}{n^\frac{999}{1000}}} = 100 \times 1,000 \lim_{n \to \infty} \frac{\frac{1}{n} \cdot \log^{99} n}{\frac{1}{n^\frac{999}{1000}}} = 100 \times 1,000 \lim_{n \to \infty} \frac{\log^{99} n}{n^{.001}} \end {aligned} \)

One hundred applications of L’Hospital’s rule yields:

\( \begin{aligned} (100 \times 1,000) \times (99 \times 1,000) \times \dots \times (2 \times 1,000) \times (1 \times 1,000) \times \lim_{n \to \infty} \frac{\frac{1}{n}}{n^{.001}} = (100! \times 1,000^{100}) \times \lim_{n \to \infty} \frac{1}{n^{1.001}} \end {aligned} \)

The factor \(\begin{aligned} 100! \times 1,000^{100} \end{aligned}\) is just a constant and \(\begin{aligned} \lim_{n \to \infty} \frac{1}{n^{1.001}} \end{aligned} = 0\) and so

\( \begin{aligned} \lim_{n \to \infty} \frac{f(n)}{g(n)} = \lim_{n \to \infty} \frac{\log^{100} n}{n^{.001}} = 0 \end {aligned} \)

Therefore

\(f = o(g) \implies f = O(g)\)

where the antecedent states that function \(f\) grows slower than a constant multiple of function \(g\) for all sufficiently large \(n\)

and the consequent states that function \(f\) grows no faster than a constant multiple of function \(g\) for all sufficiently large \(n\)

Hide code cell source
s = 10
n = np.linspace(0.1, s, 1001)
f = np.log(n)**10
g = n**(0.001)

fig = plt.figure();
ax  = plt.subplot();
ax.set_aspect(1);
ax.plot(n, f, label='', linewidth=2, color='#6495ED');
ax.plot(n, g, label='', linewidth=2, color='#ED7864');
ax.set_facecolor('#c0c6d1')
ax.set_xlim(0,s);
ax.set_ylim(0,s);
../../../../_images/98456a3527268a7b68198f5607f53401af33274279efc9c81127081a665f4529.png