Maths for Algorithms: Logs, Powers, Summations

By the end of this lesson you will be able to compute and reason about powers and logarithms by hand, read and evaluate Σ (sigma) summation notation, derive closed forms for the sums that show up when you count loop operations, compare the growth rates of the functions you'll meet constantly in algorithm analysis (log n, √n, n, n log n, n², n³, 2ⁿ, n!), and follow — and reproduce — a simple proof by mathematical induction. This is the toolbox the rest of the Algorithms track assumes you already own.

1. Powers: doubling and the chessboard

Legend has it that the inventor of chess asked the king for a "modest" reward: place 1 grain of rice on square 1 of the chessboard, 2 grains on square 2, 4 on square 3, doubling every square, all the way to square 64. The king laughed at how small this sounded — and then his treasurer told him it would take more rice than exists on Earth. That is the whole idea of a power: 2ⁿ just means "start with 1 and double it, n times". Doubling feels slow at first and explodes later — that explosion is the single most important shape in this entire course.

bⁿ ("b to the power n") means multiply b by itself n times: 2³ = 2×2×2 = 8. b is the base, n is the exponent. Watch the grains pile up one square at a time:

In Dart: the same doubling as a loop, and as 1 << n (a left shift is "multiply by 2ⁿ"); the chessboard total needs BigInt because 2⁶³ no longer fits a 64-bit int.

Input size → what's feasible: n ≤ 62 → plain int holds 2ⁿ; n up to 64 (or thousands) → use BigInt; n = 10⁹ → you can only talk about the exponent, never write the number.

2ⁿ is not the same as 2×n. 2¹⁰ = 1024 (doubled ten times), while 2×10 = 20. Confusing the two is one of the most common beginner slips — always ask "am I doubling, or just adding?"

Three exponent rules let you combine powers without ever multiplying the whole thing out — and the verify file checks every one of these numerically:

RuleStatementWorked example
Productbᵐ · bⁿ = bᵐ⁺ⁿ3⁴ · 3⁵ = 3⁹ (81 · 243 = 19683)
Power of a power(bᵐ)ⁿ = bᵐⁿ(2³)⁴ = 2¹² (8⁴ = 4096 = 2¹²)
Quotientbᵐ ÷ bⁿ = bᵐ⁻ⁿ (m ≥ n)5⁷ ÷ 5³ = 5⁴
Zero exponentb⁰ = 1 for any b ≠ 0the "empty product": multiplying nothing together leaves 1
Negative exponentb⁻ⁿ = 1 / bⁿ2⁻³ = 1/8

In Dart: each exponent rule as an equality that the program checks on concrete numbers (b⁰ = 1 is the loop that never runs, b⁻ⁿ needs a double because it is a fraction).

Input size → what's feasible: exponents up to ≈ 62 for base 2 (≈ 39 for base 3) before a 64-bit int overflows; for bigger ones use BigInt or modular arithmetic.

The product rule (bᵐ·bⁿ=bᵐ⁺ⁿ) is why logarithms turn multiplication into addition — that single fact is the entire reason logarithms were invented (to make hand calculation of huge products easier), and it is why nested loops that each contribute a power of the same base combine so cleanly.

2. Logarithms: "how many times can I halve it?"

A logarithm answers the mirror-image question to a power. 2ⁿ = 64 asks "double 1, how many times, to reach 64?" — the answer is 6, and we write log₂ 64 = 6. Think of a guessing game with only "higher/lower" answers: to find a hidden number among 64 options by always asking "is it in the top half or bottom half?", you need exactly 6 questions, because each question halves the remaining possibilities. That is precisely why binary search, and dozens of other algorithms, run in "log n" time — they are repeatedly halving a search space.

Formally, log_b(x) = y means "b raised to the power y gives x", i.e. bʸ = x. In computer science the default base is almost always 2 (because computers split things in half or store bits), so we abbreviate lg n = log₂ n.

In Dart: log_b x = y means b^y = x. For base 2 the code is literally "count the halvings"; the guessing game is a binary search over 1…64.

Input size → what's feasible: x ≤ 10¹⁸ → at most 59 halvings; so a loop (or x.bitLength - 1) is O(log x), trivial even for 10⁶ queries.

RuleStatementWhy (from the matching exponent rule)
Productlog_b(xy) = log_b x + log_b ymultiplying values adds their exponents
Quotientlog_b(x/y) = log_b x − log_b ydividing values subtracts exponents
Powerlog_b(xᵏ) = k · log_b xrepeating a base k times multiplies the exponent by k
Change of baselog_b x = ln x / ln b (any base works, e.g. log10)proven & checked numerically in verify/m01.dart

In Dart: the three log rules and change of base, each typed as lg(...) arithmetic and compared on concrete numbers (the page's 8 · 4 = 32 example, plus all x, y in 1…50). Dart has no built-in log2 for doubles, so lg x = math.log(x) / math.ln2 is the change-of-base formula itself.

Input size → what's feasible: x, y ≤ 10⁹ → all of these are O(1) calls to math.log; the answers are doubles, so compare with a tolerance (here 10⁻⁹), never with ==.

Why the base doesn't matter in Big-O: log_b x = log_2 x / log_2 b — switching base only multiplies by the constant 1/log_2 b. Since Big-O notation throws away constant multipliers entirely (Section 5), O(log₂ n), O(log₁₀ n) and O(ln n) are all exactly the same Big-O class. That's why algorithm books almost never bother writing the base at all — they just write O(log n).

In Dart: the "base is only a constant factor" claim, as code: the ratio log_b x / log_2 x is the same number for every x.

Input size → what's feasible: any x up to 10¹² → ratio unchanged, which is why Big-O ignores the base.

ln and e: e ≈ 2.71828… is a special constant that shows up naturally whenever something grows continuously in proportion to its own size (compound interest compounded infinitely often, radioactive decay, and — in this lesson — the harmonic series and Stirling's approximation). ln x = log_e x is "log base e", called the natural log. You rarely need to compute with e by hand; just recognise it as "the natural base for continuous growth", the same way 2 is the natural base for "things that double".

In Dart: e as the limit of (1 + 1/n)ⁿ, ln as the inverse of eˣ (math.exp / math.log), and Stirling's n! ≈ √(2πn)(n/e)ⁿ checked in log-space.

Input size → what's feasible: n = 10⁶ in the limit already gives 5 correct digits of e; Stirling is for n ≥ 10 (error under 1%) — and you never build n! itself, only ln n!.

lg* n ("log-star") answers: "how many times must I apply lg before the result is ≤ 1?" lg* 2 = 1 (one application: lg 2 = 1). lg* 4 = 2 (lg 4 = 2, lg 2 = 1). lg* 16 = 3. lg* 65536 = 4. Even for a number as unimaginably large as 2^65536 (a number with 19,729 digits), lg* is only 5 — each application of lg collapses the number so violently that lg* grows slower than any function you're likely to name. It shows up in the running time of certain Union-Find data structures, and the lesson mentions it mainly so the symbol doesn't look scary when you meet it later.

In Dart: lg* as "apply lg until the value is ≤ 1", plus the digit-count formula behind the "19,729 digits" and "301 million digits" claims.

Input size → what's feasible: 2⁶⁵⁵³⁶ is far too big for a double (max ≈ 1.8·10³⁰⁸) — so we reduce it by one lg by hand; BigInt can still hold it (19,729 digits).

3. Floors, ceilings & mod

Imagine a number line where you're only allowed to stand on whole numbers. Floor (⌊x⌋) means "step down to the nearest whole number you can reach without going up" — like rounding a price down. Ceiling (⌈x⌉) means "step up to the nearest whole number" — like how a lift always stops at a whole floor, never floor 2.7. These matter constantly in algorithms: "split the array in half" needs a floor or ceiling, because array lengths are whole numbers but half of an odd length isn't.
IdentityMeaning
⌊n/2⌋ + ⌈n/2⌉ = nsplitting an array of length n into two halves always accounts for every element
⌈⌈x/a⌉/b⌉ = ⌈x/(ab)⌉nested ceilings collapse into one — chaining "round up" steps is the same as one bigger round-up
a mod n = a − n⌊a/n⌋the remainder after dividing a by n (n > 0), always in [0, n) — Dart's % (a Euclidean modulo, never negative) gives exactly this

In Dart: ⌊x⌋ is x.floor(), ⌈x⌉ is x.ceil(); for integers ⌊n/2⌋ = n ~/ 2 and ⌈n/2⌉ = (n + 1) ~/ 2. a mod n is Dart's %, and the identity a mod n = a − n⌊a/n⌋ is checked with negative a too.

Input size → what's feasible: a, b, x up to 10⁹ → all O(1); ~/ truncates toward 0, so for negative numbers it is NOT floor — use .floor() or %.

Every one of these identities is checked numerically for many values of a, b, x, n in verify/m01.dart — they are not "trust me", they are "typed in and tested".

4. The growth race: which function wins?

Imagine nine runners on a track, each moving according to a different formula as the race number n increases: one stays still (constant), one crawls (lg n), a few jog (√n, n), three sprint (n lg n, n², n³), and two "runners" — 2ⁿ and n! — teleport ahead so fast they leave the track entirely within a few dozen steps. (One new symbol here: n!, "n factorial", means "multiply every whole number from n down to 1 together" — e.g. 4! = 4×3×2×1 = 24. It has nothing to do with excitement; it is just repeated multiplication, the same way Σ is repeated addition.) This is the single most useful mental picture for reading any Big-O in this entire course.

In Dart: the nine cost functions as Dart expressions; to compare 2ⁿ and n! without overflow the program compares log₂ of each (lg(n!) = Σ lg k), at n = 100 and n = 1000, and shows doubling n multiplies n² by 4 and n³ by 8.

Input size → what's feasible: n = 100 and 1000 are the "big enough" sizes where the ordering 1 < lg n < √n < n < n lg n < n² < n³ < 2ⁿ < n! is already final (at n = 10 it is not: √10 < lg 10).

To make the race concrete: suppose your computer does 100,000,000 (10⁸) basic operations per second. If your algorithm's cost matches one of these functions, here is the largest input size you could finish in exactly one second (every number below is computed and verified in verify/m01.dart). (You will see the notation O(...) used as a column label below before it is formally defined in Section 5 — for now just read O(f(n)) as "the cost is, roughly, the function f(n)"; the precise definition is coming.)

Cost functionLargest n in 1 second (10⁸ ops)
O(1)unlimited — the cost never depends on n at all
O(lg n)effectively unlimited — solving budget = lg n for n gives n = 2^(100,000,000), a number with roughly 30 million digits, and it would still finish in one second
O(√n)10,000,000,000,000,000 (10¹⁶)
O(n)100,000,000
O(n lg n)4,523,071
O(n²)10,000
O(n³)464
O(2ⁿ)26
O(n!)11

In Dart: the table above computed by largestN(cost, hi): a binary search for the biggest n whose cost fits in the 10⁸-operation budget (cost must increase with n). Doubling the budget is also tested.

Input size → what's feasible: binary search over [1, hi] takes ≤ 60 cost evaluations, so it is instant; the bound hi must keep the cost inside a 64-bit int (n³ → hi = 10⁶).

Look how brutal the drop is: an O(n²) algorithm handles ~10 thousand items in a second, but an O(n!) algorithm can barely handle 11. Doubling your computer's speed only doubles n for O(n) — but for O(2ⁿ) it only adds 1 to the largest solvable n. No amount of "buy a faster computer" rescues an exponential algorithm; you need a fundamentally better algorithm instead.

5. Big-O, Θ, Ω: the sandwich picture

Picture your algorithm's real running time f(n) as a wiggly line that's hard to describe exactly. Big-O draws a ceiling above it (f(n) ≤ c·g(n) eventually) — "it never grows faster than this". Big-Ω draws a floor below it (f(n) ≥ c·g(n) eventually) — "it never grows slower than this". Big-Θ is both at once — a sandwich, squeezing f(n) tightly between a floor and a ceiling of the same shape. This is exactly the formal definition, stated as a picture instead of formulas.

In Dart: the definitions as checkers: isBigO / isOmega / isTheta test f(n) ≤ c·g(n) (or ≥) for every n from n₀ upward. A program can only test a finite range, so it can refute bad witnesses (n₀ = 2 fails) but a proof still needs algebra.

Input size → what's feasible: witnesses c₁ = 1, c₂ = 2, n₀ = 4 for f(n) = n² + 3n; test range n ≤ 10⁵ (10⁵ steps) is plenty to expose a wrong constant.

The Growth of Functions lesson in the Algorithms track is the formal home of these definitions, including the two one-sided cousins: o(g) ("strictly less than, in the limit") and ω(g) ("strictly more than, in the limit"). This lesson gives you the picture; that lesson gives you the proofs.

6. Summations (Σ)

Σ (capital Greek sigma) is just algebra's word for a for loop that adds things up. Σ_{i=1}^{n} f(i) means: "start a running total at 0, then for i = 1, 2, 3, … up to n, add f(i) to the total." The little number under Σ is where the loop starts, the number on top is where it stops (inclusive), and the expression after Σ is what gets added each time — exactly like for (i = 1; i <= n; i++) total += f(i);

In Dart: Σ is a for loop with a running total; an empty range adds nothing. For a sequence a₁…aₙ written in math notation (1-indexed) the Dart list is a[0]…a[n-1], so aᵢ is a[i - 1].

Input size → what's feasible: n ≤ 10⁸ → a plain loop is fine (~1 s); n = 10¹⁸ or millions of queries → you need a closed form (next sections).

6.1 Arithmetic series: 1 + 2 + 3 + … + n

As a 9-year-old, Carl Friedrich Gauss was reportedly asked to add 1 to 100 by hand — and instantly answered 5050 by pairing the first and last numbers: 1+100=101, 2+99=101, 3+98=101… fifty pairs, each summing to 101.

General form: Σ_{i=1}^{n} i = n(n+1)/2. With n numbers making n/2 pairs worth (n+1) each, the total is n(n+1)/2 — verified against a real loop for n = 0…200 in verify/m01.dart.

There is a similar closed form for the sum of squares (Gauss's pairing does not apply to it; it is proved by induction in the same style as Section 8): Σ_{i=1}^{n} i² = n(n+1)(2n+1)/6. For n=10 that's 10·11·21/6 = 385, matching 1+4+9+…+100 exactly.

In Dart: Gauss's pairing (n/2 pairs each worth n + 1), the closed form n(n+1)/2 for n = 0…200 and the sum of squares n(n+1)(2n+1)/6 for n = 0…100, each compared with the real loop.

Input size → what's feasible: n ≤ 3·10⁹ → n(n+1) still fits a 64-bit int (≈ 9·10¹⁸) and the O(1) formula beats the O(n) loop; for Σi² the product n(n+1)(2n+1) fits up to n ≈ 1.6·10⁶.

6.2 Geometric series: 1 + 2 + 4 + … and 1 + ½ + ¼ + …

A geometric series multiplies by a fixed ratio each step instead of adding a fixed amount. When the ratio is exactly 2, you get exactly the chessboard staircase from Section 1:

Σ_{k=0}^{n} 2^k = 2^{n+1} − 1 — one less than the very next power of two. This is why n bits can represent all integers from 0 up to 2ⁿ−1: those 2ⁿ values are exactly the totals of every possible subset of bit-values 1,2,4,…,2ⁿ⁻¹.

When the ratio is a fraction less than 1, the staircase shrinks instead of grows — and something remarkable happens: it never exceeds a fixed ceiling, no matter how many terms you add. (New symbol: ∞, "infinity", is not a number you can reach — it just means "and keep going, forever, with no last term." Writing ∞ on top of a Σ, as in Σ_{k=0}^{∞}, means "add up one term for every whole number k = 0, 1, 2, 3, … without ever stopping.")

Infinite decreasing geometric series: Σ_{k=0}^{∞} x^k = 1/(1−x) for |x| < 1. With x = 1/2: 1 + ½ + ¼ + ⅛ + … = 1/(1−½) = 2. No matter how many halves you add, you can never reach or pass 2 — you just get closer forever. (General finite form used above: Σ_{k=0}^{n} x^k = (x^{n+1}−1)/(x−1) for any ratio x ≠ 1 — this is the formula verified in verify/m01.dart for bases other than 2 too, e.g. base 3.)

In Dart: the staircase Σ 2ᵏ = 2ⁿ⁺¹ − 1, the "n bits hold 0…2ⁿ−1" subset-sum claim for {1, 2, 4, 8}, the general ratio formula (xⁿ⁺¹ − 1)/(x − 1) (exact, with BigInt) and the infinite series 1/(1−x) by summing 2000 terms.

Input size → what's feasible: n ≤ 61 for base 2 in a plain int; other bases or larger n → BigInt; infinite series: 2000 terms is enough when |x| ≤ 0.9 (0.9²⁰⁰⁰ ≈ 10⁻⁹²).

6.3 The harmonic series: 1 + ½ + ⅓ + … ≈ ln n

Unlike the geometric series, adding smaller and smaller fractions 1 + ½ + ⅓ + ¼ + … never settles down — it keeps growing forever, just extremely slowly. Picture the bars 1/k as columns standing on a graph next to the smooth curve y = 1/x: the total area under all the columns is almost exactly the area under the curve from 1 to n, and that curve's area is ln n (a basic calculus fact — the area under 1/x is the definition of the natural log).
Harmonic number Hₙ = Σ_{k=1}^{n} 1/k satisfies ln(n+1) ≤ Hₙ ≤ ln(n) + 1 — sandwiched exactly like a Big-Θ bound, both sides verified numerically for n up to 5000. It grows like ln n, so in Big-Θ terms Hₙ = Θ(lg n) (natural log and log base 2 differ only by the constant 1/ln 2).

In Dart: Hₙ as a loop, the bound ln(n+1) ≤ Hₙ ≤ ln n + 1 for every n ≤ 5000, and the doubling-block split ([1,1], [2,2], [3,4], [5,8], …) where every block adds at most 1.

Input size → what's feasible: n ≤ 10⁷ → summing 1/k is a fast loop (error ≈ 10⁻⁹); Hₙ ≈ 16.7 at n = 10⁷, so it never gets large; the block split needs ⌈lg n⌉ + 1 = 25 blocks there.

6.4 Telescoping sums

Some sums collapse almost entirely when you rewrite each term as a difference, because the "middle" of each term cancels the "start" of the next one — like a collapsible telescope sliding shut.

Σ_{k=1}^{n} 1/(k(k+1)) = Σ (1/k − 1/(k+1)) = 1 − 1/(n+1). Writing out the terms: (1−½)+(½−⅓)+(⅓−¼)+… — every middle term appears once with a + and once with a −, cancelling completely, leaving only the very first +1 and the very last −1/(n+1).

In Dart: the partial-fraction identity 1/(k(k+1)) = 1/k − 1/(k+1), the sum 1 − 1/(n+1) for n up to 500, and the general rule Σ(aₖ − aₖ₋₁) = aₙ − a₀ on a concrete list.

Input size → what's feasible: n up to 10⁸ → the closed form is O(1); the loop would also work but accumulates rounding error in double.

6.5 Bounding a sum by an integral, and splitting a sum

Two more tools, used constantly to get quick Θ-bounds without finding an exact closed form:

Integral approximation (A.11–A.12): for a monotonic f: if f is increasing, ∫_{m−1}^{n} f(x)dx ≤ Σ_{k=m}^{n} f(k) ≤ ∫_m^{n+1} f(x)dx; if f is decreasing, the inequalities flip sides: ∫_m^{n+1} f(x)dx ≤ Σ_{k=m}^{n} f(k) ≤ ∫_{m−1}^{n} f(x)dx. The harmonic bound uses the decreasing case with f = 1/x: the rectangles are sandwiched between integrals of the smooth curve, both of which are ln expressions (the first term 1 is kept apart because ∫ dx/x blows up at 0, giving ln(n+1) ≤ Hₙ ≤ 1 + ln n).

Splitting a sum (A.2, used for the harmonic Θ(lg n) bound): break the range 1..n into blocks that double in size — [1,1], [2,2], [3,4], [5,8], [9,16], … — there are only ⌈lg n⌉+1 such blocks (11 for n = 1000), and inside each block every term 1/k is at most the block's first term, so each block contributes at most 1 to the sum. Total Hₙ ≤ ⌈lg n⌉ + 1 (at most that many blocks × 1) — an upper bound built entirely from doubling, with no calculus at all. Both the integral bound and the splitting bound are verified numerically in verify/m01.dart.

In Dart: the two integral sandwiches with exact antiderivatives (x³/3 for the increasing f(x) = x², ln x for the decreasing f(x) = 1/x), tested for m = 2…10 and n up to 200, and the harmonic bound they imply.

Input size → what's feasible: any n: numerical integration is not needed — you use the antiderivative; tests here use n ≤ 200 (≈ 2000 loop steps).

6.6 From nested loops to Σ: counting operations

This is the single most practical use of summations in this whole course: counting exactly how many times a piece of code runs.

For for i in 1..n: for j in i..n: op(), the inner loop runs n−i+1 times for each i, so the total is Σ_{i=1}^{n} (n−i+1) = n + (n−1) + … + 1 = n(n+1)/2 — the exact same arithmetic series as Gauss's sum, just counted from the other direction. Whenever you see a nested loop whose inner bound depends on the outer counter, reach for Σ.

In Dart: the nested loop itself, its Σ (n − i + 1) form and n(n+1)/2, all compared for n = 1…60 and n = 1000 (500 500 iterations).

Input size → what's feasible: n ≤ 10⁴ → the brute-force count is fine (≈ 5·10⁷ steps); n = 10⁵ → 5·10⁹ steps is too slow, use the formula.

7. Recurrences preview: T(n) = 2T(n/2) + n

Some algorithms (like merge sort) split the problem into two half-size pieces, solve each recursively, then do n work to combine the results. Drawing this as a tree — one node per recursive call, labelled with the work done at that call — is the fastest way to "see" the total cost without algebra.
Each level of the tree does a total of exactly n work (twice as many nodes, each doing half as much), and there are lg n + 1 levels (because the problem size halves each level, and Section 2 already told you how many times you can halve n before hitting 1). Total work = n per level × (lg n + 1) levels = Θ(n lg n) — verified against the exact recursive definition in verify/m01.dart. This is exactly the recurrence merge sort satisfies; the Divide & Conquer lesson covers the general master method for recurrences of this shape.

In Dart: T(n) = 2T(n/2) + n as a recursive function, the same with a memo table, and the closed form n·lg n + n (lg n is j for n = 2ʲ); the tree for n = 16 has lg n + 1 = 5 levels of exactly n work.

Input size → what's feasible: n = 2²⁰ → recursion depth only 21 (safe) and 2²¹ calls; n must be a power of two for this exact formula.

8. Proof by induction: dominoes

Mathematical induction proves a statement is true for every whole number using exactly the logic of a row of falling dominoes: (1) knock over the first domino — prove the statement for the smallest case (the base case). (2) show each domino knocks over the next one — prove that if the statement holds for some n, it must also hold for n+1 (the inductive step). If both hold, every domino falls, forever — the statement is true for all n from the base case onward, even though you never touched dominoes 2 through a million individually.
Worked proof: Σ_{i=1}^{n} i = n(n+1)/2 for all n ≥ 1.
Base case (n=1): left side = 1. Right side = 1·2/2 = 1. Equal. ✅
Inductive hypothesis: assume the formula holds for some n = k, i.e. Σ_{i=1}^{k} i = k(k+1)/2.
Inductive step (show it then holds for n = k+1):
Σ_{i=1}^{k+1} i = (Σ_{i=1}^{k} i) + (k+1) = k(k+1)/2 + (k+1) (by the inductive hypothesis)
= (k+1)(k/2 + 1) = (k+1)(k+2)/2, which is exactly the formula with n replaced by k+1.
Conclusion: since the base case holds and each case implies the next, the formula holds for every n ≥ 1. ∎ (Every algebraic step above is re-checked numerically for k = 1…500 in verify/m01.dart.)

In Dart: what the proof says, executed: the base case n = 1, then for k = 1…500 the hypothesis, the algebra step k(k+1) + 2(k+1) = (k+1)(k+2) and the step P(k) + (k+1) = P(k+1). (Checking 500 cases is evidence, not proof — the induction argument is what covers every n.)

Input size → what's feasible: k ≤ 500 here; the proof itself covers every n ≥ 1.

Quiz

Interview questions

Cheat sheet

TopicKey facts
Powersbᵐ·bⁿ=bᵐ⁺ⁿ, (bᵐ)ⁿ=bᵐⁿ, b⁰=1, b⁻ⁿ=1/bⁿ
Logslog_b(xy)=log_b x+log_b y, log_b(xᵏ)=k·log_b x, log_b x = ln x/ln b (base is a constant factor — irrelevant in Big-O)
Floors/ceilings⌊n/2⌋+⌈n/2⌉=n; nested floors/ceilings of the same kind collapse: ⌈⌈x/a⌉/b⌉=⌈x/(ab)⌉
Growth order (small→large)1, lg n, √n, n, n lg n, n², n³, 2ⁿ, n!
O / Θ / ΩO = upper bound (ceiling), Ω = lower bound (floor), Θ = both (sandwich)
Arithmetic seriesΣi = n(n+1)/2; Σi² = n(n+1)(2n+1)/6
Geometric seriesΣ_{k=0}^{n} x^k=(x^{n+1}-1)/(x-1); infinite (|x|<1): 1/(1-x)
Harmonic seriesHₙ = Σ1/k ≈ ln n; precisely ln(n+1) ≤ Hₙ ≤ ln n + 1
Telescopingrewrite terms as differences so the middle cancels: Σ(aₖ−aₖ₋₁)=aₙ−a₀
Recurrence T(n)=2T(n/2)+nΘ(n lg n) — n work per level × lg n + 1 levels
Inductionbase case + "true for k ⇒ true for k+1" ⇒ true for all n from the base case up