Summations and sequences
Sums and sequences are how running times are added up. This lesson covers arithmetic and geometric progressions, closed formulas, prefix sums, the harmonic series, telescoping sums and the Fibonacci sequence.
Learning goals
- Evaluate arithmetic and geometric sums in closed form
- Use prefix sums for range queries
- Recognise the harmonic series in loop analysis
- Compute Fibonacci numbers efficiently and safely
Prerequisites
Arithmetic progression
An arithmetic progression has a constant difference d: a, a + d, a + 2d, …. Its sum is the number of terms times the average of the first and last: S = n(a + l) / 2. For 1 + 2 + … + n this is n(n + 1) / 2, which is why a nested loop where the inner bound grows by one is O(n²).
Gauss paired the first and last terms, which each sum to n + 1. Step through the pairing.
Gauss's trick: 1 + 2 + … + n
Interactive · edit the inputint[] arr · length 10
pair sums
running total
Add 10 numbers one by one and you do 10 additions. Gauss noticed that numbers equidistant from the two ends always add to the same value, n + 1 = 11.
1static long sumTo(int n) { // 1 + 2 + ... + n2long total = 0;3for (int i = 1; i <= n; i++) total += i; // n additions4return total;5}67static long sumToFormula(int n) {8return (long) n * (n + 1) / 2; // n/2 pairs, each summing to n + 19}
Live state
n
10
Execution trace (dry run table)Show 7 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=10 | Add 10 numbers one by one and you do 10 additions. Gauss noticed that numbers equidistant from the two ends always add to the same value, n + 1 = 11. |
| 2 | L8 | lo=1 hi=10 pair=11 total=11 | 1 + 10 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 3 | L8 | lo=2 hi=9 pair=11 total=22 | 2 + 9 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 4 | L8 | lo=3 hi=8 pair=11 total=33 | 3 + 8 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 5 | L8 | lo=4 hi=7 pair=11 total=44 | 4 + 7 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 6 | L8 | lo=5 hi=6 pair=11 total=55 | 5 + 6 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 7 | L8 | n(n+1)/2=55 total=55 return=55 | 10 × 11 / 2 = 55, the same as the loop's total 55. The loop is O(n); the formula is O(1). Use long: n(n + 1) overflows int for n above about 46,340. |
Geometric progression
A geometric progression multiplies by a ratio r: a, ar, ar², …. The sum of n terms is a(rⁿ - 1) / (r - 1) for r ≠ 1. With r = 2: 1 + 2 + 4 + … + 2ᵏ⁻¹ = 2ᵏ - 1, so the total work in the last level of a binary tree is about the same as all the levels above it combined.
For |r| < 1 the infinite sum converges to a / (1 - r); for example n + n/2 + n/4 + … < 2n, which explains why halving costs total O(n).
| Series | Closed form | Order |
|---|---|---|
| 1 + 2 + 4 + … + 2ᵏ | 2ᵏ⁺¹ - 1 | Θ(2ᵏ) |
| n + n/2 + n/4 + … | < 2n | Θ(n) |
| 1 + 3 + 9 + … + 3ᵏ | (3ᵏ⁺¹ - 1) / 2 | Θ(3ᵏ) |
Sum formulas
Three formulas cover most loop analysis: Σi = n(n + 1)/2, Σi² = n(n + 1)(2n + 1)/6 and Σi³ = (n(n + 1)/2)². They show that a triple loop whose bounds grow is Θ(n³), and that Σi² grows like n³/3.
Check any formula with small n before relying on it: Σi² for n = 4 is 1 + 4 + 9 + 16 = 30 and 4·5·9/6 = 30. Use long when n is large because n(n + 1) overflows int above about 46,340.
1long n = 100_000;2long sum = n * (n + 1) / 2; // 50000500003long sumSq = n * (n + 1) * (2 * n + 1) / 6; // 333338333350000
Gauss's trick: 1 + 2 + … + n
Interactive · edit the inputint[] arr · length 10
pair sums
running total
Add 10 numbers one by one and you do 10 additions. Gauss noticed that numbers equidistant from the two ends always add to the same value, n + 1 = 11.
1static long sumTo(int n) { // 1 + 2 + ... + n2long total = 0;3for (int i = 1; i <= n; i++) total += i; // n additions4return total;5}67static long sumToFormula(int n) {8return (long) n * (n + 1) / 2; // n/2 pairs, each summing to n + 19}
Live state
n
10
Execution trace (dry run table)Show 7 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=10 | Add 10 numbers one by one and you do 10 additions. Gauss noticed that numbers equidistant from the two ends always add to the same value, n + 1 = 11. |
| 2 | L8 | lo=1 hi=10 pair=11 total=11 | 1 + 10 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 3 | L8 | lo=2 hi=9 pair=11 total=22 | 2 + 9 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 4 | L8 | lo=3 hi=8 pair=11 total=33 | 3 + 8 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 5 | L8 | lo=4 hi=7 pair=11 total=44 | 4 + 7 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 6 | L8 | lo=5 hi=6 pair=11 total=55 | 5 + 6 = 11. Pair the next two from the ends: every pair equals n + 1. |
| 7 | L8 | n(n+1)/2=55 total=55 return=55 | 10 × 11 / 2 = 55, the same as the loop's total 55. The loop is O(n); the formula is O(1). Use long: n(n + 1) overflows int for n above about 46,340. |
Prefix sums
A prefix-sum array stores p[i] = sum of the first i elements. Any range sum then costs O(1): sum(l..r) = p[r + 1] - p[l]. Building takes O(n), so q queries cost O(n + q) rather than O(nq).
Change the array and query range in the animation to see the subtraction.
1int[] a = {3, 1, 4, 1, 5};2long[] p = new long[a.length + 1];3for (int i = 0; i < a.length; i++) p[i + 1] = p[i] + a[i]; // [0, 3, 4, 8, 9, 14]4long range = p[4] - p[1]; // sum of a[1..3] = 1 + 4 + 1 = 6
Prefix sums and O(1) range queries
Interactive · edit the inputint[] arr · length 8
prefix[]
prefix has n + 1 slots. prefix[0] = 0 is the sum of nothing, which removes every special case for l = 0.
1static int[] buildPrefix(int[] arr) {2int[] prefix = new int[arr.length + 1]; // prefix[i] = arr[0] + ... + arr[i-1]3for (int i = 0; i < arr.length; i++) {4prefix[i + 1] = prefix[i] + arr[i];5}6return prefix;7}89static int rangeSum(int[] prefix, int l, int r) { // sum of arr[l..r]10return prefix[r + 1] - prefix[l];11}
Live state
n
8
Execution trace (dry run table)Show 10 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=8 | prefix has n + 1 slots. prefix[0] = 0 is the sum of nothing, which removes every special case for l = 0. |
| 2 | L4 | i=0 arr[i]=3 prefix[i+1]=3 | prefix[1] = prefix[0] + arr[0] = 0 + 3 = 3. |
| 3 | L4 | i=1 arr[i]=-1 prefix[i+1]=2 | prefix[2] = prefix[1] + arr[1] = 3 + -1 = 2. |
| 4 | L4 | i=2 arr[i]=4 prefix[i+1]=6 | prefix[3] = prefix[2] + arr[2] = 2 + 4 = 6. |
| 5 | L4 | i=3 arr[i]=1 prefix[i+1]=7 | prefix[4] = prefix[3] + arr[3] = 6 + 1 = 7. |
| 6 | L4 | i=4 arr[i]=5 prefix[i+1]=12 | prefix[5] = prefix[4] + arr[4] = 7 + 5 = 12. |
| 7 | L4 | i=5 arr[i]=9 prefix[i+1]=21 | prefix[6] = prefix[5] + arr[5] = 12 + 9 = 21. |
| 8 | L4 | i=6 arr[i]=-2 prefix[i+1]=19 | prefix[7] = prefix[6] + arr[6] = 21 + -2 = 19. |
| 9 | L4 | i=7 arr[i]=6 prefix[i+1]=25 | prefix[8] = prefix[7] + arr[7] = 19 + 6 = 25. |
| 10 | L10 | l=2 r=5 prefix[r+1]=21 prefix[l]=2 return=19 | Sum of arr[2..5] = prefix[6] - prefix[2] = 21 - 2 = 19. Building took O(n) once; every query afterwards is O(1). |
Harmonic series
The harmonic series Hₙ = 1 + 1/2 + 1/3 + … + 1/n grows like ln n + 0.5772 (so about 14.4 at n = 10⁶). It shows up in loops such as for i in 1..n: for j = i; j <= n; j += i, which run n/1 + n/2 + … + n/n = n·Hₙ = O(n log n) steps. The sieve of Eratosthenes has the same structure.
Because Hₙ grows so slowly, n·Hₙ is only a little more than n.
1int n = 1_000_000; long steps = 0;2for (int i = 1; i <= n; i++) for (int j = i; j <= n; j += i) steps++;3// steps = 13970034 = about n * (ln n + 0.577) = 1e6 * 14.39
Telescoping sums
A telescoping sum has terms that cancel in pairs, leaving only the ends. Since 1/(i(i + 1)) = 1/i - 1/(i + 1), the sum over i = 1..n equals 1 - 1/(n + 1). Differences in a prefix-sum array telescope in the same way: Σ(p[i + 1] - p[i]) = p[n] - p[0].
Look for a term that can be written as f(i) - f(i + 1).
1// sum_{i=1..n} 1/(i(i+1)) = 1 - 1/(n+1)2int n = 9;3double s = 0;4for (int i = 1; i <= n; i++) s += 1.0 / (i * (i + 1));5// s = 0.9 = 1 - 1/10
Fibonacci sequence
F(0) = 0, F(1) = 1, F(n) = F(n - 1) + F(n - 2): 0, 1, 1, 2, 3, 5, 8, 13, …. Naive recursion repeats work exponentially; memoisation or a simple loop computes F(n) in O(n) time and O(1) space. F(92) = 7,540,113,804,746,346,429 is the largest value that fits in long.
Compare the call counts with and without memoisation in the animation.
1static long fib(int n) { // valid for n <= 922long a = 0, b = 1;3for (int i = 0; i < n; i++) { long t = a + b; a = b; b = t; }4return a;5}
Fibonacci with memoization
Interactive · edit the inputRecursion tree · 1 call
Call fib(6).
1static long[] memo = new long[100];2static long fib(int n) {3if (n <= 1) return n; // base cases4if (memo[n] != 0) return memo[n]; // already solved: reuse it5return memo[n] = fib(n - 1) + fib(n - 2); // solve once, remember6}
Live state
n
6
depth
1
Call stack (top first)
- fib(6)
Execution trace (dry run table)Show 52 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L2 | n=6 depth=1 | Call fib(6). |
| 2 | L3 | n=6 | Not a base case. |
| 3 | L4 | n=6 memo[n]=0 | memo[6] is empty, so compute it. |
| 4 | L5 | n=6 | fib(6) = fib(5) + fib(4). Call fib(5) first. |
| 5 | L2 | n=5 depth=2 | Call fib(5). |
| 6 | L3 | n=5 | Not a base case. |
| 7 | L4 | n=5 memo[n]=0 | memo[5] is empty, so compute it. |
| 8 | L5 | n=5 | fib(5) = fib(4) + fib(3). Call fib(4) first. |
| 9 | L2 | n=4 depth=3 | Call fib(4). |
| 10 | L3 | n=4 | Not a base case. |
| 11 | L4 | n=4 memo[n]=0 | memo[4] is empty, so compute it. |
| 12 | L5 | n=4 | fib(4) = fib(3) + fib(2). Call fib(3) first. |
| 13 | L2 | n=3 depth=4 | Call fib(3). |
| 14 | L3 | n=3 | Not a base case. |
| 15 | L4 | n=3 memo[n]=0 | memo[3] is empty, so compute it. |
| 16 | L5 | n=3 | fib(3) = fib(2) + fib(1). Call fib(2) first. |
| 17 | L2 | n=2 depth=5 | Call fib(2). |
| 18 | L3 | n=2 | Not a base case. |
| 19 | L4 | n=2 memo[n]=0 | memo[2] is empty, so compute it. |
| 20 | L5 | n=2 | fib(2) = fib(1) + fib(0). Call fib(1) first. |
| 21 | L2 | n=1 depth=6 | Call fib(1). |
| 22 | L3 | n=1 | Base case: fib(1) = 1. |
| 23 | L3 | n=1 return=1 | return 1. |
| 24 | L5 | n=2 left=1 | fib(1) = 1. Now call fib(0). |
| 25 | L2 | n=0 depth=6 | Call fib(0). |
| 26 | L3 | n=0 | Base case: fib(0) = 0. |
| 27 | L3 | n=0 return=0 | return 0. |
| 28 | L5 | n=2 left=1 right=0 return=1 | fib(2) = 1 + 0 = 1. Store it in memo[2]. |
| 29 | L5 | n=3 left=1 | fib(2) = 1. Now call fib(1). |
| 30 | L2 | n=1 depth=5 | Call fib(1). |
| 31 | L3 | n=1 | Base case: fib(1) = 1. |
| 32 | L3 | n=1 return=1 | return 1. |
| 33 | L5 | n=3 left=1 right=1 return=2 | fib(3) = 1 + 1 = 2. Store it in memo[3]. |
| 34 | L5 | n=4 left=2 | fib(3) = 2. Now call fib(2). |
| 35 | L2 | n=2 depth=4 | Call fib(2). |
| 36 | L3 | n=2 | Not a base case. |
| 37 | L4 | n=2 memo[n]=1 | memo[2] is already 1. No need to recurse again. |
| 38 | L4 | n=2 return=1 | Cache hit: return 1 immediately. This whole subtree is skipped. |
| 39 | L5 | n=4 left=2 right=1 return=3 | fib(4) = 2 + 1 = 3. Store it in memo[4]. |
| 40 | L5 | n=5 left=3 | fib(4) = 3. Now call fib(3). |
| 41 | L2 | n=3 depth=3 | Call fib(3). |
| 42 | L3 | n=3 | Not a base case. |
| 43 | L4 | n=3 memo[n]=2 | memo[3] is already 2. No need to recurse again. |
| 44 | L4 | n=3 return=2 | Cache hit: return 2 immediately. This whole subtree is skipped. |
| 45 | L5 | n=5 left=3 right=2 return=5 | fib(5) = 3 + 2 = 5. Store it in memo[5]. |
| 46 | L5 | n=6 left=5 | fib(5) = 5. Now call fib(4). |
| 47 | L2 | n=4 depth=2 | Call fib(4). |
| 48 | L3 | n=4 | Not a base case. |
| 49 | L4 | n=4 memo[n]=3 | memo[4] is already 3. No need to recurse again. |
| 50 | L4 | n=4 return=3 | Cache hit: return 3 immediately. This whole subtree is skipped. |
| 51 | L5 | n=6 left=5 right=3 return=8 | fib(6) = 5 + 3 = 8. Store it in memo[6]. |
| 52 | L5 | return=8 | fib(6) = 8 with 11 calls. Plain recursion would need 25 calls. |
Practice ladder
Now try it yourself
Easy
- Sum 5 + 8 + 11 + … for 20 terms
- Compute Σi² for n = 10
Medium
- Answer 10⁵ range-sum queries with prefix sums
- Show that n + n/2 + n/4 + … is less than 2n
Advanced
- Compute F(n) mod 10⁹ + 7 for n = 10¹⁸ with matrix exponentiation
Key takeaways
- n(n + 1)/2, 2ᵏ - 1 and n·Hₙ are the three sums behind most loop analysis.
- Prefix sums turn range sums into subtraction.
- Fibonacci fits in long up to F(92).