Question 1 · easy
Which statement about 5n² + 3n is TRUE?
Answer: option C. n³ is a valid, if loose, upper bound; the tight bound is Θ(n²).
One short symbol for a growth story: O(1), O(log n), O(n), O(n log n), O(n²), O(2ⁿ).
How can we describe how the work grows with more input?
T(n) is O(g(n)) when some constants c > 0 and n₀ make T(n) ≤ c g(n) for every n ≥ n₀. Here c = 5 and n₀ = 1. Big-O does not mean an exact time.
| Notation | What it promises | Example |
|---|---|---|
| O(g) | eventual upper bound | 3n + 2 is O(n), also O(n²) |
| Ω(g) | eventual lower bound | 3n + 2 is Ω(n) |
| Θ(g) | both bounds, same growth | 3n + 2 is Θ(n) |
Keep the fastest-growing term; constant factors do not change Θ. Changing the base of a logarithm changes only a constant factor.
| Linear search, n > 0 | Equality checks |
|---|---|
| Best: first item matches | 1 |
| Worst: absent or last | n |
| Average: target present, each position equally likely | (n + 1)/2 |
Big-O itself does not mean “worst case”. You can bound the best, average or worst-case count. An average needs a model of which inputs occur.
def doubled(values):
return [2 * x for x in values]This function creates n new entries. A loop that only keeps a running sum uses Θ(1) extra storage under the fixed-size-number model. State whether output storage is included.
10 test questions · 11 written questions · 21 total
Each question is followed by its answer and explanation. Hard questions also include a starting hint and smaller reasoning steps.
Choose one option. Cost questions state their model and whether the bound is tight, expected or worst-case. Here lg means log₂, and heap positions start at 1.
Question 1 · easy
Which statement about 5n² + 3n is TRUE?
Answer: option C. n³ is a valid, if loose, upper bound; the tight bound is Θ(n²).
Question 2 · easy
The Big-Theta of 4n³ + 100n² + 7 is:
Answer: option B. Keep the fastest-growing term and drop its constant.
Question 3 · medium
Which pair of constants proves 3n² + 10n = O(n²)?
Answer: option B. 3n² + 10n ≤ 4n² exactly when n ≥ 10. (a) never holds, (c) never holds, and (d) fails at n = 1.
Question 4 · medium
Which function eventually grows fastest as n tends to infinity?
Answer: option B. An exponential dominates every polynomial, however high its degree.
Question 5 · medium · course question
What does f(n) = O(g(n)) state for nonnegative cost functions?
Answer: option B. Big-O is an eventual upper bound: there are constants c and n₀ such that f(n) ≤ c·g(n) whenever n ≥ n₀.
Question 6 · medium · course question
What is the tight growth class of 8n + 100?
Answer: option D. The linear term controls eventual growth. A fixed coefficient and a fixed additive cost do not change Θ(n).
Question 7 · medium · course question
Which statement distinguishes Big-O from Big-Theta correctly?
Answer: option A. Both notations can describe a selected best-, average- or worst-case cost function. Their difference is upper-only versus matching two-sided growth bounds.
Question 8 · medium · course question
Which pair proves 2n + 5 ≤ c*n for every integer n ≥ n₀?
Answer: option C. With c=3 the inequality becomes 5 ≤ n, so n₀=5 works. The other coefficients cannot bound 2n+5 for all sufficiently large n.
Question 9 · medium · course question
Two algorithms have costs n² and 100n. What is justified as n grows without bound?
Answer: option B. For n > 100, n² > 100n. Constants can matter for small inputs, but eventual growth still separates these functions.
Question 10 · hard
Which statement is FALSE?
In simpler words: Compare eventual growth, not values at one small input.
Starting hint: Any fixed positive power of n eventually beats any fixed power of log n.
Answer: option C. Any positive power of n, including √n, dominates every power of log n.
Read each question together with its explanation, trace or proof. Numbering continues from the test questions.
Question 11 · easy
True or false: 5n² + 3n = O(n³).
true. Big-O is an upper bound, and n³ is a valid (though not tight) upper bound; the tight statement is 5n² + 3n = Θ(n²).
Question 12 · easy
Is 100n = O(n²)? Is n² = O(100n)?
yes (100n ≤ n² once n ≥ 100); no (n² eventually exceeds any constant multiple of n).
Question 13 · easy
Give the Big-Theta of 7n³ − 2n² + 40, and of 3n log n + 50n.
Θ(n³) and Θ(n log n). Keep the fastest-growing term, drop its constant.
Question 14 · easy
Order these from slowest-growing to fastest: 2ⁿ, n log n, n!, log n, n², n, 1.
1, log n, n, n log n, n², 2ⁿ, n!.
Question 15 · medium
Find constants c and n₀ that prove 3n² + 10n = O(n²).
c = 4 and n₀ = 10 work: 3n² + 10n ≤ 4n² is the same as 10n ≤ n², which holds whenever n ≥ 10. (Many other pairs work too, such as c = 13, n₀ = 1.)
Question 16 · medium
Prove that n² is not O(n).
suppose n² ≤ c·n for all n ≥ n₀. Dividing by n gives n ≤ c for all n ≥ n₀, which is false for any n larger than both c and n₀. So no constants exist.
Question 17 · medium
Which grows faster: n^1.01 or n log n? And 2ⁿ or n¹⁰⁰?
n^1.01 eventually grows faster than n log n because log n / n^0.01 tends to zero. Also 2ⁿ eventually exceeds n¹⁰⁰. Near n = 1,000 they are close; for integer n ≥ 1, the final crossover is at n = 997. These are eventual-growth statements, not rankings at every small n.
Question 18 · medium
If f(n) = O(n²) and g(n) = O(n log n), what can you say about f(n) + g(n) and about f(n) · g(n)?
f + g = O(n²) (the bigger term wins in a sum) and f · g = O(n³ log n) (products multiply).
Question 19 · hard
Prove that Big-O is transitive: if f(n) = O(g(n)) and g(n) = O(h(n)), then f(n) = O(h(n)).
In simpler words: Show that two eventual upper bounds can be chained.
Starting hint: Write the two inequalities and choose a point where both are valid.
there are c₁, n₁ with f(n) ≤ c₁ g(n) for n ≥ n₁, and c₂, n₂ with g(n) ≤ c₂ h(n) for n ≥ n₂. For n ≥ max(n₁, n₂), f(n) ≤ c₁ g(n) ≤ c₁ c₂ h(n). So the constant c₁c₂ and threshold max(n₁, n₂) witness f = O(h).
Question 20 · hard
Explain why log n grows slower than nᵏ for every k > 0, however small k is.
In simpler words: Compare a logarithm with even a tiny positive power.
Starting hint: Replace n by 2ᵐ so the logarithm becomes m.
write n = 2ᵐ. Then log n = m while nᵏ = 2ᵏᵐ = (2ᵏ)ᵐ, an exponential in m with base 2ᵏ > 1. An exponential in m always dominates the linear function m, so the ratio log n / nᵏ tends to 0.
Question 21 · hard
Let H(n) = 1 + 1/2 + 1/3 + … + 1/n (the harmonic number from the quicksort analysis). Show without calculus that H(n) = Θ(log n).
In simpler words: Estimate 1 + 1/2 + … + 1/n without adding each term.
Starting hint: Group denominators between consecutive powers of two.
group the terms into blocks: (1), (1/2), (1/3 + 1/4), (1/5 + … + 1/8), …, the k-th block holding the 2ᵏ⁻¹ terms from 1/(2ᵏ⁻¹ + 1) to 1/2ᵏ. Each block’s terms are each at most 1/2ᵏ⁻¹ and at least 1/2ᵏ, so each block sums to at most 1 and at least 1/2. There are about lg n blocks, so H(n) is between roughly (lg n)/2 and lg n + 1: Θ(log n).
Predict on paper, run the code, then explain any difference. Use the animations below to inspect individual steps.
For T(n) = 3n + 2, verify the bound 5n at n = 1, 2 and 10.
T = 5, 8, 32; bounds = 5, 10, 50. Algebra 2 ≤ 2n proves it for all n ≥ 1.
Find average equality checks for a present target uniformly placed in 9 items.
(9 + 1)/2 = 5. This depends on the stated distribution.
Change a running-sum function to return all prefix sums. What happens to result storage?
It grows from one result to n results: Θ(n) storage, assuming fixed-size numbers.
Give the Big-O class of each, in terms of the length of data:
a = len(data)
b = data[len(data) // 2]
c = max(data)
d = sorted(data)
e = [x * 2 for x in data]
f = [(x, y) for x in data for y in data]
a O(1) — Python stores the length. b O(1) — arithmetic plus
one lookup. c O(n) — must inspect every item. d O(n log n).
e O(n). f O(n²) — and it builds a list of n² pairs, so it
eats memory at the same rate.
Take the four ratio columns you measured in week 6 and write the Big-O class each one implies. Then look at the code and confirm the class from its shape. Any disagreement is a finding worth a paragraph.
Assume a quadratic time model T(n) = c n² takes 1.0 second at n = 10 000. Predict its time at n = 40 000, then test the prediction on a bounded implementation. Do the same for a linear model T(n) = c n. Big-O upper bounds alone do not determine these ratios.
Four times the data on a quadratic curve is sixteen times the work: about 16 seconds. The linear one goes to about 4 seconds. Expect real measurements a bit above the prediction — memory effects usually work against the bigger run.
Give the overall time class of each function, showing the per-region receipt you added up. Then say the space class of the second one.
def one(data):
s = sum(data) # ?
data.sort() # ?
return s, data[0]
def two(data):
seen = []
for x in data: # ?
if x not in seen: # ?
seen.append(x)
return seen
one: sum is O(n), sort is O(n log n), the
return is O(1). Add and keep the biggest → O(n log n).
two: the loop runs n times, and not in seen is itself an
O(n) scan, so it is O(n) × O(n) = O(n²) time. Its space is
O(n) because seen can grow to the size of the input.
(Week 11's set turns that membership test into O(1) and the whole thing into O(n).)
For the find function in §8.6, and a list of 1 000 items, give the exact
number of comparisons in the best case, the worst case, and the average case when the
target is present and equally likely to be anywhere. Then say the Big-O of each.
Best: 1 comparison, O(1). Worst: 1 000 comparisons, O(n). Average: the target is at position 1 to 1 000 with equal chance, so the exact mean is (1 + 1 000) / 2 = 500.5 comparisons — still O(n) once the ½ constant is dropped.
T(n) = 7n + 300. Which is the conventional simplified notation after removing constants and lower-order terms?
Which grows fastest as n becomes large?
You write for item in queries: and inside it if item in big_list:. If both hold n items, the class is:
Under this course’s default convention, "this search is O(n)" refers to which case?
A function does one O(n) loop, then a separate O(n²) double loop, then returns. Its conventional simplified upper bound is:
A function walks a list once keeping a single running maximum. Its time and space are:
An inner loop written as for j in range(10): sits inside a loop over n items. The pair is:
Last week you learned to reduce a step count to its dominant term with the constants stripped off. Big-O is just the standard way of writing that result down:
T(n) = 5n² + 200n + 3000 → O(n²)
Read it aloud as "oh of n squared" or "order n squared". It means an eventual upper bound: for some fixed c > 0 and n₀, the cost is at most c n² for every n ≥ n₀. It need not grow exactly in proportion to n²; linear cost is also O(n²). Use Θ(n²) for matching quadratic upper and lower bounds. A bound describes growth under a stated cost model, not a duration in seconds.
The multiplier, the smaller terms, your CPU, your programming language, and whether n is small. It answers exactly one question: how does the cost respond to more data? Every other question needs the stopwatch as well.
| Notation | Name | In plain words | Everyday example | n doubles ⇒ time |
|---|---|---|---|---|
O(1) | constant | Cost does not depend on the input size at all | Reading data[5]; a formula | unchanged |
O(log n) | logarithmic | Each doubling of the data adds one step | The dictionary halving trick | +1 step |
O(n) | linear | One visit per item | Summing a list; searching for a missing item | ×2 |
O(n log n) | linearithmic | A pass over the data, repeated log n times | Good sorting: sorted() | slightly more than ×2 |
O(n²) | quadratic | Every item meets every item | Comparing all pairs; bubble sort | ×4 |
O(2ⁿ) | exponential | Each extra item doubles the total work | Trying every combination | squared — hopeless |
The rows illustrate growth models such as T(n) = c n and T(n) = c n². Their doubling responses describe those models, not every function satisfying the listed upper bound. Drag the slider to explore hypothetical durations at ten million equal-cost steps per second; these are model calculations, not benchmarks:
At n = 1 000 000 in this model, n steps take 0.1 s, while n² steps take 100 000 s, about 27.8 hours. That million-fold difference shows why the algorithmic strategy can matter much more than a modest hardware speedup.
This is the step people take on faith at first, so it is worth doing once with real numbers. Take a made-up step count with a fat constant and a big linear term bolted on:
T(n) = 3n² + 500n + 8000
Watch which term actually decides the total as n grows. The table shows what each term contributes and, in the last column, the share the n² term takes:
| n | 3n² | 500n | 8000 | share from 3n² |
|---|---|---|---|---|
| 10 | 300 | 5 000 | 8 000 | 2% |
| 100 | 30 000 | 50 000 | 8 000 | 34% |
| 1 000 | 3 000 000 | 500 000 | 8 000 | 85% |
| 10 000 | 300 000 000 | 5 000 000 | 8 000 | 98% |
| 1 000 000 | 3 × 10¹² | 5 × 10⁸ | 8 000 | 99.98% |
At small n the constant 8000 is the biggest thing on the row and the "slow" n² term is invisible. But every column except the first eventually surrenders to n²: the constant never moves, the linear term is left in the dust, and by n = 1 000 000 the squared term is the answer to two decimal places. Big-O reports the term that wins this race, because that is the term that decides what happens when the data grows — which is the only question Big-O is asked.
And the multiplier? Drop it because it does not change the shape. Double the
input on any n² curve — whether it is n², 3n² or
1000n² — and the time goes up four-fold. The 3 sets the height of the
curve; the n² sets its bend. Big-O is a claim about the bend.
To go from T(n) to Big-O: keep the fastest-growing term, throw away its
multiplier. 7n + 3 → O(n). n²/2 − n → O(n²).
4n log n + 90n → O(n log n). Everything else on the line is noise once
n is large.
Four questions, in order. They resolve most everyday code:
O(1).O(n).O(n²).O(log n), or O(n log n) if that halving happens inside a pass over the data.# A
def first_item(data):
return data[0]
# B
def total(data):
result = 0
for x in data:
result += x
return result
# C
def any_duplicate(data):
for i in range(len(data)):
for j in range(i + 1, len(data)):
if data[i] == data[j]:
return True
return False
# D
def halvings(n):
count = 0
while n > 1:
n = n // 2
count += 1
return count
A is O(1): one shelf lookup, whatever the length. B is O(n):
one visit each. C is O(n²): every pair, in the worst case — note that
"return True early" helps only when duplicates exist. We are explicitly bounding
worst-case time here; Big-O itself does not choose an input case.
D is O(log n): the value halves every round.
Some innocent-looking lines contain a loop inside Python itself. x in my_list
is O(n). sorted(data) is O(n log n).
data.insert(0, x) is O(n). Put any of those inside your own
loop and you have quietly written nested loops. This is the single most common way
beginners produce accidentally quadratic code, and weeks 10–11 are devoted to it.
When the shape is not obvious at a glance, fall back on two mechanical rules and add the costs up like a shopping receipt:
Here is a function built to exercise both rules. Price it one region at a time:
def report(data): # n = len(data)
n = len(data) # O(1) — length is stored
total = 0
for x in data: # O(n) — one pass
total += x
biggest_gap = 0
for i in range(n): # outer runs n times…
for j in range(n): # …inner runs n times each → O(n²)
gap = abs(data[i] - data[j])
if gap > biggest_gap:
biggest_gap = gap
top10 = sorted(data)[-10:] # O(n log n) sort, then O(1) slice
return total, biggest_gap, top10
Reading top to bottom, the receipt is:
O(1) + O(n) + O(n²) + O(n log n).
Sequential parts add, so we keep the biggest — and n² beats
n log n beats n beats 1. The whole function is
O(n²), and the culprit is the one nested loop. Everything else is along
for the ride.
The first loop and the double loop are written one after another, so they
add: O(n) + O(n²), which is still O(n²). Only loops written
inside each other multiply. It is worth pointing at the indentation and
saying which rule applies — that habit alone prevents most misreadings.
for j in range(10):?
Assuming n ≥ 10 so those indices exist (or using range(min(10, n))),
the inner loop has at most ten steps, so the double loop is
O(n) × O(10) = O(10n) = O(n). The whole function would drop to
O(n log n), decided by the sort. A loop bound that does not grow with the
data is a constant, and constants leave.
Searching a list of n items for a value:
O(1).O(n).O(n) once constants go.In this course, an unqualified running-time statement uses the worst case by convention. Big-O itself means an upper bound, and can be applied to best-case, worst-case or expected cost. State the case; for an average, also state the input distribution, such as a successful target uniformly distributed over the positions.
def find(data, target):
for i, x in enumerate(data):
if x == target:
return i # stops the moment it hits
return -1
nums = list(range(1000))
print(find(nums, 0)) # best case: 1 comparison
print(find(nums, 999)) # worst case: 1000 comparisons
print(find(nums, -1)) # worst case: 1000 comparisons, absentSame function, three wildly different amounts of work, and the input alone decides which you get. When you report "linear search is O(n)", you are quoting the middle and third lines — the promise that holds even for the unlucky user.
Systems fail at their worst moments, not their average ones. A search that is normally instant but occasionally scans ten million rows will be discovered by your users at the busiest hour of the year.
Big-O is not only about time. The same notation describes how much extra memory a method needs as the input grows — its space complexity. When you quote a cost, it is fair to ask which one you mean, because a method can be cheap in one and dear in the other.
"Extra" is the key word: it means memory beyond the input you were handed.
def reverse_in_place(data):
i, j = 0, len(data) - 1
while i < j: # swaps ends inward
data[i], data[j] = data[j], data[i]
i += 1
j -= 1
# uses two index variables, whatever n is → O(1) extra space
def reverse_copy(data):
result = []
for x in data:
result = [x] + result # copies the growing result and adds one item
return result # → O(n) extra space
The first reverses the supplied list in place and returns None; its extra
space is O(1) and its time is O(n). The second returns a new reversed
list, leaving the input unchanged, with O(n) peak extra space. Its
repeated prepending copies 1 + 2 + … + n references, so this particular copying
implementation takes Θ(n²) time. Time and peak memory are different accounts.
Every time your code builds a new list, string or dictionary whose size follows n,
that is O(n) space. The list comprehension [(x, y) for x in data for y in
data] from earlier is O(n²) in both time and space — it constructs n²
pairs and has to hold them all. Week 9's counting solution spends O(1) extra space
(two fixed 26-slot lists) to buy O(n) time; that is the trade named in full next
week.
Cover the answers and call each one before you read on. These are the shapes you will meet again and again; the goal is to name them on sight.
# 1
def middle(data):
return data[len(data) // 2]
# 2
def pair_sums(data):
out = []
for x in data:
for y in data:
out.append(x + y)
return out
# 3
def count_down(n):
while n > 1:
n = n // 2
print(n)
# 4
def has_common(a, b):
for x in a: # len(a) = n
if x in b: # 'in' on a list = O(len(b))
return True
return False
# 5
def sort_then_scan(data):
data = sorted(data) # O(n log n)
for x in data: # O(n)
print(x)
# 6
def first_three(data):
return data[0], data[1], data[2]
1 — O(1): one division, one lookup, length is free.
2 — O(n²) in time and O(n²) in space; it stores every pair.
3 — O(log n): the value halves each round.
4 — O(n × m), and if both lists are length n that is O(n²): the
hidden loop inside in nests under the visible loop.
5 — O(n log n): sort dominates the later linear scan.
6 — O(1): three fixed lookups, no dependence on length.
| Mistake | Why it is wrong |
|---|---|
| "O(2n) is worse than O(n)" | They are the same class. Constants are dropped. |
| "O(n²) is always slower than O(n)" | Upper bounds alone do not guarantee a crossover. Under tight linear versus quadratic models, constants determine where eventual growth dominates. |
| "Nested loops are always O(n²)" | Two full n-iteration loops with constant-cost bodies give n² work. Other nested bounds require counting; a fixed inner length 10 contributes a constant factor. |
| "Two loops in a row is O(n²)" | Sequential loops add, not multiply: O(n) + O(n) = O(n). Only nested loops multiply. |
| "Big-O tells me how many seconds" | It tells you the shape of the curve, not a point on it. You still need the stopwatch. |
| "My code has no loops, so it is O(1)" | Check the built-in calls: in, sorted, max, sum, slicing and copying all walk the data. |
| "O(n) time means O(n) memory" | Different questions. A pass that keeps one running total is O(n) time but O(1) space. |
The midterm gives you short code snippets and measurement tables. You will count steps, name classes, and justify each answer in one sentence. Nothing needs to be memorised that is not in this checklist.
So far O has done all the work, and for everyday code it is all you need. But the textbook — and the exercises below — use four relatives of O, written with Greek letters. They sound intimidating and mean something very simple. Big-O only ever says "grows no faster than" — it is a ceiling. The cousins fill in the other directions: a floor, an exact match, and two strict versions.
| Symbol | Say it | In plain words | Everyday analogy |
|---|---|---|---|
O(g) | "big-oh" | grows no faster than g — an upper bound, a ceiling | a speed limit: you never go above it |
Ω(g) | "big-omega" | grows no slower than g — a lower bound, a floor | a minimum wage: you never earn below it |
Θ(g) | "big-theta" | grows at exactly the same rate as g — ceiling and floor at once | two runners who stay side by side the whole race |
o(g) | "little-oh" | grows strictly slower than g — falls hopelessly behind | a pedestrian next to a car: the gap only widens |
ω(g) | "little-omega" | grows strictly faster than g — pulls away without limit | the car, seen from the pedestrian's side |
Two of these you already understand. O is the ceiling you have used all
week; Θ (theta) is the honest one — it says "same shape, up to a
constant", which is what you mean when you say a function "really is" quadratic. The
difference between the big and little versions is the word strictly:
n = O(n) is true (n grows no faster than itself), but
n = o(n) is false (n does not grow strictly slower than itself). Little-o
and little-ω are for when one function leaves the other in the dust for good.
Think of comparing two numbers. O is like ≤ (at most),
Ω is ≥ (at least), Θ is = (equal),
o is < (strictly less), and ω is > (strictly
greater) — but applied to growth rates as n heads to infinity, ignoring
constant multipliers. If f = Θ(g) then automatically
f = O(g) and f = Ω(g), exactly as "equal" implies
both "at most" and "at least".
Every exercise below is really the same skill: line the two functions up against a
fixed ladder of growth rates and see which rung each one lands on. Here is that ladder,
slowest on the left, and it is worth memorising — ≪ here means "grows
strictly slower than":
1 ≪ log n ≪ √n ≪ n ≪ n log n ≪ n² ≪ n³ ≪ 2ⁿ ≪ 3ⁿ ≪ n!
Read left to right, each item is little-o of everything to its right and little-ω of everything to its left. Anything at the same rung (only differing by a constant factor) is Θ of its neighbour on that rung. Almost every "which grows faster?" question is answered by pointing at this line. Two facts do most of the heavy lifting: any polynomial beats any logarithm, and any exponential beats any polynomial.
These finish the ordering problems from the chapter. They look scary because sums and odd exponents appear — but each piece collapses to something on the growth ladder from §8.15. The trick is always the same: replace each expression by the simplest thing it grows like, then line them up.
Order these from slowest- to fastest-growing:
f1 = n² log₂ nf2 = n (log₂ n)²f3 = 2⁰ + 2¹ + … + 2ⁿ (a sum of powers of two)f4 = log₂(2⁰ + 2¹ + … + 2ⁿ)First simplify the two that hide a sum:
f3 is a geometric sum: 1 + 2 + 4 + … + 2ⁿ = 2ⁿ⁺¹ − 1, which grows like 2ⁿ (exponential).f4 is the log of that, and log₂(2ⁿ⁺¹) = n + 1, which grows like n (linear).Now compare the four simplified shapes: n, n(log n)², n² log n, 2ⁿ.
To compare f2 = n(log n)² with f1 = n² log n, divide both by n log n: you get log n versus n — and log n is far smaller, so f2 < f1.
Answer: f4 (≈ n) < f2 (n log² n) < f1 (n² log n) < f3 (2ⁿ).
Order these from slowest- to fastest-growing, and say if any are the same order:
f1 = √1 + √2 + … + √n (a sum of square roots)f2 = (√n)log nf3 = n√(log n)f4 = 12·n3/2 + 4nSimplify each:
f1: adding up √i for i = 1..n is like the area under √x, which grows like n3/2 (about ⅔·n3/2).f4 = 12 n3/2 + 4n — keep the top term: also n3/2. So f1 and f4 are the same order.f2 = (√n)log n = n(log₂ n)/2 — the exponent itself grows with n, so this beats every fixed power of n.f3 = n√(log n) — the exponent √(log n) also grows, so this too beats every fixed power — but more slowly than f2, because (log n)/2 eventually dwarfs √(log n).Answer: f1 = f4 (both n3/2) < f3 (n√log n) < f2 (n(log n)/2). The key idea: a fixed power like n3/2 is always beaten by n raised to a power that itself keeps growing.
From the beginner notes · Lectures 2, 3
Big-O gives an upper limit on growth once the input is large enough. To show 2n² + 6n is O(n²), choose c = 3 and n₀ = 6. For n ≥ 6, the linear term 6n is at most n², so the whole expression is at most 3n².
Big-Omega gives a lower limit on growth for large enough inputs. Big-Theta means the upper and lower limits have the same growth pattern. A quadratic function is also O(n³), but that upper bound is loose. Naming a bound is different from selecting a best, average or worst input case: apply the notation to the input case and type of work you chose to count.
Test your reasoning with a claim that fails. If n² were O(n), then n² ≤ cn would imply n ≤ c for every sufficiently large n. No fixed c can satisfy that.
Engineering use. When an AI labels a loop O(n), require it to define n, identify the repeated operation and justify the bound.
00:00–01:00: launch, predict–run–explain cycle, questions · 01:00–01:10: break · 01:10–02:00: worked variation, peer instruction, questions · 02:00–02:10: break · 02:10–03:00: core mechatronics practice, exam bridge, and exit ticket.
Ask at any point. Weekly self-checks stay private; optional extensions are not collected.
Need a slower explanation? Open the English + Türkçe reference guide.
AA_Week08.ipynb and complete Tasks 1–5.report function from §8.5, type it into Colab, and add a comment above every region naming its class. Then change the inner loop bound from n to 10 and write one sentence on how the overall class changes and why.| Term | Meaning in plain words |
|---|---|
| Big-O | A short label for how cost grows with input size. |
| constant / logarithmic / linear | O(1) / O(log n) / O(n). |
| linearithmic | O(n log n) — the speed of good sorting. |
| quadratic / exponential | O(n²) — every pair. O(2ⁿ) — every combination; unusable beyond tiny n. |
| dominant term | The fastest-growing part of T(n); the one Big-O keeps. |
| worst case | The most work the method can be forced to do; the default meaning of a Big-O claim. |
| space complexity | How the extra memory a method needs grows with the input. |
| hidden loop | A built-in operation that walks the data even though you wrote no loop. |
These are the Big-O exercises from Chapter 2 of Steven Skiena's The Algorithm
Design Manual, worked here at first-year level. Skiena writes lg for
the base-2 logarithm and ln for the natural one; for Big-O purposes the
base changes a logarithm by a constant multiplier. This preserves its own growth
class, but a logarithm inside an exponent requires more care; use base 2 for
unspecified logs in these exercises. A formal proof must justify its inequality
for every input beyond a fixed threshold. Numerical examples illustrate that
argument but cannot replace it. Cover each answer and try it first.
How many times is r incremented, and what is the running time in Big-O?
r = 0
for i in range(1, n + 1):
for j in range(1, i + 1):
for k in range(j, i + j + 1):
for l in range(1, (i + j - k) + 1):
r += 1Do not panic at the messy bounds. Count the loops: there are four, nested one inside the other, and each one runs a number of times that is at worst proportional to n. Every extra nested loop that ranges up to about n multiplies the total count by another factor of about n, so four of them give roughly n × n × n × n = n⁴. The exact starting points shift the constant in front but not the shape.
The clean test is to count the increments and double n. If it is really n⁴, doubling n should multiply the count by about 2⁴ = 16:
def prestiferous(n):
r = 0
for i in range(1, n + 1):
for j in range(1, i + 1):
for k in range(j, i + j + 1):
for l in range(1, (i + j - k) + 1):
r += 1
return r
prev = None
for n in [5, 10, 20, 40, 80]:
r = prestiferous(n)
ratio = "-" if prev is None else f"{r / prev:.2f}x"
print(f"n={n:>3} r={r:>12,} ratio={ratio}")
prev = rThe ratio climbs steadily towards 16 — the unmistakable fingerprint of n⁴. (It approaches 16 from below rather than sitting on it exactly because of the lower-order terms, just as the doubling harness behaved in weeks 5–7.)
Optional challenge: the exact closed form can be worked out with the summation techniques of §8.16's later problems, but the marks are for the class, not the formula.
Answer: peeling the loops from the inside gives the exact value r = n(n+1)(n+2)(3n+1)/24, so the running time is Θ(n⁴). (The loop depth only hints at the class; summing the loops proves it.)
How many times is r incremented, and what is the worst-case running time?
r = 0
for i in range(1, n + 1):
for j in range(i + 1, n + 1):
for k in range(i + j - 1, n + 1):
r += 1Again, ignore the awkward starting points and count the loops: three, nested, each ranging over a stretch that is at worst about n long. Three full n-sized loops would give n³ work, but dependent bounds can change the growth rate. Here the sum derived below establishes a cubic leading term. That predicts doubling n will eventually multiply the count by about 2³ = 8:
def conundrum(n):
r = 0
for i in range(1, n + 1):
for j in range(i + 1, n + 1):
for k in range(i + j - 1, n + 1):
r += 1
return r
prev = None
for n in [10, 20, 40, 80, 160]:
r = conundrum(n)
ratio = "-" if prev is None else f"{r / prev:.2f}x"
print(f"n={n:>3} r={r:>10,} ratio={ratio}")
prev = rThe ratio settles towards 8 — cubic confirmed by arithmetic, no stopwatch needed.
Answer: the moving start points empty many iterations, leaving the exact count r = Σi=1⌊n/2⌋ (n−2i+1)(n−2i+2)/2 (a closed form m(m+1)(4m−1)/6 for even n = 2m), so the worst-case running time is Θ(n³). Depth alone would not prove this — the ranges must be summed.
(a) Is 2ⁿ⁺¹ = O(2ⁿ)? (b) Is 2²ⁿ = O(2ⁿ)?
(a) True. 2ⁿ⁺¹ = 2 · 2ⁿ —
that is just 2ⁿ with a constant factor of 2 bolted on, and Big-O
ignores constant factors. Take c = 2 and 2ⁿ⁺¹
≤ 2 · 2ⁿ holds for every n.
(b) False. 2²ⁿ = (2ⁿ)² = 4ⁿ,
which is a completely different, faster exponential. The ratio
4ⁿ / 2ⁿ = 2ⁿ shoots off to infinity, so no fixed
constant c can ever keep 4ⁿ ≤ c · 2ⁿ.
Doubling the exponent is not a constant factor — it changes the base.
Answer: (a) true; (b) false.
For each pair, is f = O(g), f = Ω(g), or f = Θ(g)? (When both O and Ω hold, the honest answer is Θ.)
Read each pair straight off the ladder.
| f | g | Verdict | Why | |
|---|---|---|---|---|
| (a) | log(n²) | log n + 5 | Θ | log n² = 2 log n; both are log n up to a constant |
| (b) | √n | log(n²) = 2 log n | Ω | √n beats any logarithm |
| (c) | (log n)² | log n | Ω | squaring a growing thing makes it grow faster |
| (d) | n | (log n)² | Ω | a polynomial beats any power of a logarithm |
| (e) | n log n + n | log n | Ω | n log n is far above log n |
| (f) | 10 | log 10 | Θ | both are fixed constants — neither grows |
| (g) | 2ⁿ | 10 n² | Ω | an exponential beats any polynomial |
| (h) | 2ⁿ | 3ⁿ | O | 2ⁿ grows slower than 3ⁿ (ratio (2/3)ⁿ→0) |
Answer: (a) Θ · (b) Ω · (c) Ω · (d) Ω · (e) Ω · (f) Θ · (g) Ω · (h) O.
For each pair decide whether f = O(g), g = O(f), or both (which is Θ).
| f | g | Verdict | Why | |
|---|---|---|---|---|
| (a) | (n² − n)/2 | 6n | g = O(f) | f is quadratic (≈ n²/2), g is only linear |
| (b) | n + 2√n | n² | f = O(g) | f ≈ n, well below n² |
| (c) | n log n | n√n / 2 | f = O(g) | √n grows faster than log n, so n√n beats n log n |
| (d) | n + log n | √n | g = O(f) | f ≈ n, above √n |
| (e) | 2 · (log n)² | log n + 1 | g = O(f) | (log n)² grows faster than log n |
| (f) | 4n log n + n | (n² − n)/2 | f = O(g) | n log n is below n² |
None of these pairs is a tie, so Θ never applies here. Answer: (a) g=O(f) · (b) f=O(g) · (c) f=O(g) · (d) g=O(f) · (e) g=O(f) · (f) f=O(g).
Show that n³ − 3n² − n + 1 = Θ(n³).
Θ(n³) means "sandwiched between two constant multiples of n³ once n is large". The clean trick is to divide the whole expression by n³ and watch what happens as n grows:
(n³ − 3n² − n + 1) / n³ = 1 − 3/n − 1/n² + 1/n³
Each of the trailing fractions heads to 0 as n grows, so the whole ratio heads to
1. That means for large enough n the expression is close to n³
— certainly between, say, 0.5 n³ and 1 · n³.
For every n ≥ 10, 3/n ≤ 0.3 and 1/n² ≤ 0.01, so the ratio is at least
1 − 0.3 − 0.01 = 0.69 ≥ 0.5. Also −3n² − n + 1 ≤ 0 for n ≥ 1, giving
the upper bound n³. Thus c₁ = 0.5, c₂ = 1 and n₀ = 10 work for all later n.
At n = 10 the value 691 lies between 500 and 1000; this is a check, not the proof.
Answer: Θ(n³).
Show that n² = O(2ⁿ).
This is the headline fact of the ladder: an exponential eventually overtakes any
polynomial and never looks back. To prove n² = O(2ⁿ) we just
need one constant c and a starting point n₀
such that n² ≤ c · 2ⁿ for all n ≥
n₀. Watch the two race:
| n | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 |
|---|---|---|---|---|---|---|---|---|
n² | 1 | 4 | 9 | 16 | 25 | 36 | 64 | 100 |
2ⁿ | 2 | 4 | 8 | 16 | 32 | 64 | 256 | 1024 |
At n = 4, n² = 2ⁿ = 16. Suppose n² ≤ 2ⁿ for an integer n ≥ 4. Then (n+1)² ≤ 2n² because n² − 2n − 1 ≥ 0, and hence (n+1)² ≤ 2n² ≤ 2 · 2ⁿ = 2ⁿ⁺¹. Induction establishes the inequality for every integer n ≥ 4. Thus c = 1 and n₀ = 4 are valid witnesses; the displayed table is a check on this argument.
Answer: n² = O(2ⁿ), witnessed by c = 1, n₀ = 4.
For each pair, find a positive constant c with f(n) ≤ c · g(n) for all n > 1.
The tactic each time: bound every term of f by a copy of the leading term, then compare with g.
(a) f = n² + n + 1, g = 2n³. For n > 1
each of n and 1 is at most n², so
f ≤ 3n². And 3n² ≤ 4n³ = 2 · 2n³
for n > 1. So c = 2 works comfortably.
(b) f = n√n + n² = n1.5 + n², g = n².
For n > 1, n1.5 ≤ n², so
f ≤ n² + n² = 2n². So c = 2.
(c) f = n² − n + 1, g = n²/2. Since
−n + 1 ≤ 0 for n ≥ 1, we have
f ≤ n² = 2 · (n²/2). So c = 2.
Answer: c = 2 works in all three (many other constants work too — any single witness proves the bound).
Suppose f₁ = O(g₁) and f₂ = O(g₂). Show
(2-13) f₁ + f₂ = O(g₁ + g₂); (2-15)
f₁ · f₂ = O(g₁ · g₂); and (2-14) the same
two rules hold with Ω in place of O.
These are the rules that let us analyse code one piece at a time — price each
region, then add or multiply — so they are worth seeing once. "f = O(g)"
just means there is a constant c with f ≤ c · g for
large n. So write f₁ ≤ c₁ g₁ and f₂ ≤
c₂ g₂.
Sum (2-13). Add the two inequalities:
f₁ + f₂ ≤ c₁ g₁ + c₂ g₂. Let
c be the bigger of c₁ and c₂; then the
right side is at most c(g₁ + g₂). So
f₁ + f₂ = O(g₁ + g₂). This is why sequential blocks
of code add up, and keeping the biggest is just simplifying the sum.
Product (2-15). Multiply the two inequalities (all quantities
positive): f₁ · f₂ ≤ c₁ c₂ · g₁
· g₂. The single constant c₁ c₂ does the
job, so f₁ f₂ = O(g₁ g₂). This is why a loop that
runs O(g₁) times doing O(g₂) work each time costs the product.
Ω versions (2-14). Identical argument with the inequalities
flipped: f ≥ c · g. Adding lower bounds gives a lower bound on
the sum; multiplying lower bounds gives a lower bound on the product. So both rules
hold for Ω too — and since Θ is just O and Ω together, they
hold for Θ as well.
Answer: both rules hold; they are the formal licence for the "add sequential parts, multiply nested loops" recipe from §8.5.
Prove that a polynomial ak nk + ak−1 nk−1 + … + a1 n + a0 of degree k is O(nk).
This is the formal version of "keep the highest power". Every term ai ni with i ≤ k satisfies |ai ni| ≤ |ai| nk once n ≥ 1, because a lower power of n is no larger than the top power. Add the terms up:
|ak nk + … + a0| ≤ (|ak| + … + |a0|) · nk
The bracket c = |ak| + … + |a0| is a fixed constant — it does not depend on n. So the whole polynomial is at most c · nk, which is precisely O(nk).
Answer: O(nk), with the constant being the sum of the absolute values of the coefficients.
Show that (n + a)ᵇ = Θ(nᵇ) for any constants a and b > 0.
Intuition first: when n is large, adding a fixed number a barely nudges
it — 1 000 000 + 7 is, for growth purposes, still a million. So
(n + a) and n grow at the same rate, and raising both to
the same power b keeps them at the same rate. Formally, look at the ratio:
(n + a)ᵇ / nᵇ = (1 + a/n)ᵇ → 1b = 1 as n → ∞
Because that ratio approaches 1, for large n it stays trapped between two fixed
constants (say between ½ and 2). That traps (n + a)ᵇ between
½ nᵇ and 2 nᵇ — the sandwich that
defines Θ.
Answer: (n + a)ᵇ = Θ(nᵇ).
List these from slowest- to fastest-growing, marking ties (Skiena's own set):
n 2n n lg n ln n n−n3+7n5 lg n √n en n2+lg n n2 2n−1 lg lg n n3 (lg n)2 n! n1+ε (0<ε<1)
Tidy each into its shape first: n−n3+7n5 → n5; n2+lg n → n2 (ties with n2); ln n ties lg n; 2n−1 = ½·2n ties 2n; en is above 2n (since e>2); and n1+ε sits between n lg n and n2. Then walk the ladder — remembering any real power of n beats any power of a logarithm.
Answer (braces = ties): lg lg n < {ln n = lg n} < (lg n)2 < √n < n < n lg n < n1+ε < {n2 = n2+lg n} < n3 < n5 < {2n−1 = 2n} < en < n!
Same task, with a function that shrinks and a constant hidden in the list:
√n n 2n n log n n−n3+7n5 n2+log n n2 n3 log n n1/3+log n (log n)2 n! ln n n/log n log log n (1/3)n (3/2)n 6
The traps: (1/3)n shrinks to 0 (smallest of all); 6 is a constant; and the sneaky pair (log n)2 vs n1/3 — a squared logarithm is still a logarithm, and any real power of n (even 1/3) eventually wins, so (log n)2 < n1/3. Also n/log n sits just below n, and (3/2)n < 2n.
Answer (braces = ties): (1/3)n < 6 < log log n < {log n = ln n} < (log n)2 < n1/3 < √n < n/log n < n < n log n < {n2 = n2+log n} < n3 < n5 < (3/2)n < 2n < n!
Give functions f and g satisfying each condition, or argue that none exist.
(a) f = o(g) and f ≠ Θ(g). Easy:
take f = n, g = n². n grows strictly slower than n²
(so little-o holds), and they are not the same rate (so not Θ). In fact little-o
always rules out Θ, so any little-o example works.
(b) f = Θ(g) and f = o(g). Impossible.
Θ says "same rate"; little-o says "strictly slower". A function cannot be both
exactly as fast and strictly slower than another. No such pair exists.
(c) f = Θ(g) and f ≠ O(g). Impossible.
Θ is O and Ω together, so f = Θ(g) already forces
f = O(g). You cannot have the first without the second.
(d) f = Ω(g) and f ≠ O(g). Easy:
take f = n², g = n. n² grows at least as fast as n
(Ω holds), but it is not bounded above by n (so not O). This is the usual
picture of a strict lower bound.
Answer: (a) e.g. n, n² · (b) none · (c) none · (d) e.g. n², n.
Decide each, giving a one-line reason from the ladder.
| Claim | Verdict | Why | |
|---|---|---|---|
| (a) | 2n² + 1 = O(n²) | True | a constant factor plus a lower term; still n² |
| (b) | √n = O(log n) | False | √n grows faster than log n, not slower |
| (c) | log n = O(√n) | True | a logarithm is below any power of n |
| (d) | n²(1 + √n) = O(n² log n) | False | the left side is ≈ n²·√n = n2.5, above n² log n |
| (e) | 3n² + √n = O(n²) | True | √n is a lower term; n² dominates |
| (f) | √n log n = O(n) | True | √n·log n grows slower than √n·√n = n |
| (g) | log n = O(n−1/2) | False | log n → ∞ while n−1/2 = 1/√n → 0 |
Answer: (a) T · (b) F · (c) T · (d) F · (e) T · (f) T · (g) F.
State the tightest relationship of f to g.
(a) f = n² + 3n + 4, g = 6n + 7. f is quadratic, g is
linear, so f grows faster: f = Ω(g) (and definitely not O(g)).
(b) f = n√n = n1.5, g = n² − n.
g is ≈ n², above n1.5, so f is the smaller one:
f = O(g).
(c) f = 2ⁿ − n², g = n⁴ + n². The
2ⁿ term swamps the −n² (which is a rounding
error beside it), so f grows like 2ⁿ — an exponential, above
the polynomial g: f = Ω(g).
Answer: (a) Ω · (b) O · (c) Ω.
Answer yes or no, with a one-line reason.
The key idea: O is only a ceiling on the worst case — it
never forbids an algorithm from being faster. Θ on the worst case
is stronger: it says the worst case really does reach that height.
(a) Worst case O(n²) — can it be O(n) on some
inputs? Yes. Individual inputs (like a best case) can finish far
faster; the ceiling only caps the slowest.
(b) Worst case O(n²) — can it be O(n) on
all inputs? Yes. O(n²) is just an upper
bound; an algorithm that is actually linear everywhere is still (loosely) O(n²).
O does not claim the bound is tight.
(c) Worst case Θ(n²) — can it be O(n) on
some inputs? Yes. Θ pins the worst case at
quadratic, but the best case (some inputs) can still be linear or better.
(d) Worst case Θ(n²) — can it be O(n) on
all inputs? No. Θ(n²) worst case means some
inputs genuinely force quadratic work, so not every input can be linear.
(e) Is f(n) = Θ(n²) where f = 100n² for even
n and f = 20n² − n log² n for odd n? Yes.
Every value, even or odd, sits between constant multiples of n² (roughly between
19 n² and 100 n² for large n), so the whole
function is Θ(n²) regardless of which branch it takes.
Answer: (a) yes · (b) yes · (c) yes · (d) no · (e) yes.
For each, answer with a reason. Recall log(aⁿ) = n log a.
(a) 3ⁿ = O(2ⁿ)? No. The ratio
3ⁿ / 2ⁿ = (3/2)ⁿ = 1.5ⁿ races off to infinity, so
no constant can cap it. Different bases are not a constant factor apart.
(b) log 3ⁿ = O(log 2ⁿ)? Yes. Take logs:
log 3ⁿ = n log 3 and log 2ⁿ = n log 2. Their
ratio is the fixed constant log 3 / log 2 ≈ 1.585 — a
constant factor — so each is O of the other. (Taking the log tamed the
exponential into a linear function of n.)
(c) 3ⁿ = Ω(2ⁿ)? Yes. Ω asks
whether 3ⁿ grows at least as fast as 2ⁿ — and it grows much
faster, so certainly at least as fast.
(d) log 3ⁿ = Ω(log 2ⁿ)? Yes. Same
constant-ratio picture as (b): n log 3 versus n log 2. Since
they are a constant factor apart, each is both O and Ω of the other.
Answer: (a) no · (b) yes · (c) yes · (d) yes.
Give the growth class of each sum for i running from 1 to n.
(a) ∑ 1/i. This is the harmonic number, and it is famously
≈ ln n — add the terms up and they creep towards the natural
log. So it is Θ(log n). A quick numerical check confirms the
gap Hₙ − ln n settles at a constant (about 0.577, Euler's
constant):
import math
for n in [10, 100, 1000, 10000]:
H = sum(1.0 / i for i in range(1, n + 1))
print(f"n={n:>6} H={H:.4f} ln n={math.log(n):.4f} gap={H - math.log(n):.4f}")
The gap stops moving, which means H and ln n stay a
constant apart — the signature of Θ(log n).
(b) ∑ ⌈1/i⌉. For every i ≥ 1, the value
1/i lies in (0, 1], so its ceiling ⌈1/i⌉
is exactly 1. Adding 1 a total of n times gives n. So the sum is Θ(n).
(c) ∑ log i. Summing logs is the log of the product:
∑ log i = log(1 · 2 · … · n) = log(n!),
which is Θ(n log n) (see part d).
(d) log(n!). Half of the n factors are at least n/2, so
n! is at least (n/2)n/2, giving
log(n!) ≥ (n/2) log(n/2) — on the order of n log n.
And n! is at most nn, giving
log(n!) ≤ n log n. Trapped both sides: Θ(n log n).
Answer: (a) Θ(log n) · (b) Θ(n) · (c) Θ(n log n) · (d) Θ(n log n).
For each sum over i = 1 … n, give a simple g with the sum = Θ(g).
Two facts do all the work. A sum of n terms whose largest is a power iᵇ
behaves like Θ(nᵇ⁺¹) (n copies of the top term,
roughly). And a geometric sum — each term a fixed multiple of the
last, like 4ⁱ — is dominated by its final term, so it is
Θ of that last term.
(a) ∑ (3i⁴ + 2i³ − 19i + 20). The top piece is
i⁴, and ∑ i⁴ = Θ(n⁵). So
Θ(n⁵).
(b) ∑ (3 · 4ⁱ + 2 · 3ⁱ − i¹⁹ + 20).
The geometric 4ⁱ term outgrows everything, and a geometric sum is
Θ of its last term 4ⁿ. So Θ(4ⁿ).
(c) ∑ (5i + 3 · 2ⁱ). The geometric 2ⁱ
term dominates the linear one; the sum is Θ of its last term. So
Θ(2ⁿ).
Answer: (a) Θ(n⁵) · (b) Θ(4ⁿ) · (c) Θ(2ⁿ).
Let S = ∑i=1..n 3ⁱ. Which are true?
(a) S = Θ(3n−1) ·
(b) S = Θ(3ⁿ) ·
(c) S = Θ(3n+1).
A geometric series sums to S = 3 + 3² + … + 3ⁿ =
(3n+1 − 3) / 2, which is about 3n+1/2.
The whole thing is dominated by its last term, so S = Θ(3ⁿ).
Now the trick: 3n−1, 3ⁿ and
3n+1 differ only by a factor of 3 each way — and a
constant factor of 3 is invisible to Θ. So Θ of any one of
them is Θ of all of them. All three are true.
Answer: (a), (b) and (c) are all correct — they name the same growth class up to a constant.
For each f, give a simple g with f = Θ(g).
The rule is the same one from §8.3: throw away everything except the fastest-growing term (and drop its constant).
(a) f = 1000 · 2ⁿ + 4ⁿ. 4ⁿ = (2ⁿ)²
dwarfs 2ⁿ no matter how big the constant 1000 is. So
Θ(4ⁿ).
(b) f = n + n log n + √n. Of the three, n log n grows
fastest. So Θ(n log n).
(c) f = log(n²⁰) + (log n)¹⁰. The first term is
20 log n (using log n²⁰ = 20 log n) — just a
constant times log n. The second, (log n)¹⁰, is a much higher
power of log n and wins. So Θ((log n)¹⁰).
(d) f = (0.99)ⁿ + n¹⁰⁰. A base below 1 means
(0.99)ⁿ shrinks towards 0 as n grows, so it contributes
nothing; n¹⁰⁰ is the whole story. So
Θ(n¹⁰⁰).
Answer: (a) Θ(4ⁿ) · (b) Θ(n log n) · (c) Θ((log n)¹⁰) · (d) Θ(n¹⁰⁰).
For each pair (A, B), list every relationship that holds. (Remember: if A is strictly slower than B, then both A = O(B) and the stronger A = o(B) hold.)
(a) A = n¹⁰⁰, B = 2ⁿ. A polynomial is
strictly slower than an exponential, so A = O(B) and
A = o(B).
(b) A = (lg n)¹², B = √n. Any power of a
logarithm is strictly slower than any power of n, so A = O(B) and
A = o(B).
(c) A = √n, B = ncos(πn/8). The exponent
cos(πn/8) oscillates forever between −1 and +1, so B keeps
swinging from 1/n up to n and back. Sometimes B is far above
A, sometimes far below — no single relationship holds for all large n.
None of O, o, Ω, ω, Θ apply (a genuine
"can't compare" case).
(d) A = 10ⁿ, B = 100ⁿ. Different bases with
100 > 10: the ratio (10/100)ⁿ = 0.1ⁿ → 0,
so A is strictly slower: A = O(B) and A = o(B).
(e) A = nlg n, B = (lg n)ⁿ. Compare their
logs: lg A = (lg n)², while lg B = n · lg lg n.
The second grows far faster (it has a factor of n), so B outgrows A:
A = O(B) and A = o(B).
(f) A = lg(n!), B = n lg n. From Problem 2-25,
lg(n!) = Θ(n lg n) — same rate. So A = Θ(B)
(and therefore also A = O(B) and A = Ω(B), but not the
strict little versions).
Answer: (a) O, o · (b) O, o · (c) none · (d) O, o · (e) O, o · (f) Θ (with O and Ω).
Not one of these problems needed a limit computed from scratch or an epsilon chased. Every single one came down to: place both functions on the growth ladder, strip constants, and read off which is higher — with two facts on speed-dial (a polynomial beats any logarithm; an exponential beats any polynomial), one trick for taming exponentials (take logs), and one for sums (a sum is Θ of n copies of its top term, or of its last term when it is geometric). That is the whole of asymptotic reasoning at this level.
You can read a Big-O class off code. Week 9 puts it to work: one problem, solved four ways, landing in four completely different classes.