Implementing Concrete Mathematics Summations in Python

Implementing Concrete Mathematics Summations in Python

One of the most satisfying things about mathematics is when a formula you've been staring at on paper suddenly becomes a few lines of working code. This post does exactly that — we take the summation notation from Concrete Mathematics and translate it directly into Python, step by step.

No prior calculus knowledge needed. If you're comfortable with basic algebra and have written a Python function before, you're good to go.


What Is a Summation?

A summation is just a compact way of writing "add a bunch of things up." Instead of writing \(1 + 2 + 3 + \cdots + n\), mathematicians use the capital Greek letter sigma \(\sum\) as a shorthand:

$$\sum_{k=1}^{n} k$$

Let's break down what every part of this notation means, because none of it should feel mysterious:

Part What it means
\(\sum\) "Add up everything that follows"
\(k=1\) (bottom) Start the counter \(k\) at 1
\(n\) (top) Stop when \(k\) reaches \(n\) (inclusive)
\(k\) (after \(\sum\)) The thing being added at each step — here, just \(k\) itself

So \(\sum_{k=1}^{n} k\) means: set \(k = 1\), add \(k\). Set \(k = 2\), add \(k\). Keep going until \(k = n\). For \(n = 5\) that gives \(1 + 2 + 3 + 4 + 5 = 15\).

The thing being added doesn't have to be \(k\) itself — it can be any expression involving \(k\). For example, \(\sum_{k=1}^{n} k^2\) adds up the squares: \(1 + 4 + 9 + 16 + 25 = 55\) for \(n = 5\).


Translating Summation Notation into Python

The great news is that Python's built-in sum() function maps almost directly onto summation notation. Let's build a general summation function that works for any expression.

from typing import Callable

def summation(func: Callable[[int], float], start: int, end: int) -> float:
    """Compute the sum of func(k) for k from start to end, inclusive."""
    return sum(func(k) for k in range(start, end + 1))

Let's unpack every part of this, because there's more going on than it looks:

from typing import Callable — this imports a type hint called Callable, which lets us say "this argument should be a function." It doesn't change how the code runs — it's just documentation that helps you and your editor understand what the function expects.

func: Callable[[int], float]func is the thing being summed at each step. It's a function that takes an integer \(k\) and returns a number. For example, lambda k: k just returns \(k\) itself, and lambda k: k**2 returns \(k^2\).

start: int, end: int — the lower and upper limits of the sum, corresponding to the bottom and top of the \(\sum\) sign.

range(start, end + 1) — Python's range(a, b) goes from \(a\) up to but not including \(b\). So we write end + 1 to make sure end itself is included — matching the inclusive upper limit in the summation notation.

func(k) for k in range(...) — this is a generator expression. It produces the values func(1), func(2), ..., func(end) one at a time, and sum() adds them all up.

Trying It Out

Let's compute \(\sum_{k=1}^{10} k = 1 + 2 + \cdots + 10\):

result = summation(lambda k: k, 1, 10)
print(result)
55

The lambda k: k is an anonymous function that just returns its input unchanged — it's the Python equivalent of "the thing being added is \(k\) itself." We pass it as the func argument, with start=1 and end=10.


Closed Forms — Why They Matter

The summation function above works perfectly, but it has a cost: for large \(n\), it loops through every single value of \(k\). If \(n = 1{,}000{,}000\), that's a million additions.

A closed form is a direct formula that gives you the answer in a single calculation, no matter how large \(n\) is. For the sum of integers, the closed form is:

$$\sum_{k=1}^{n} k = \frac{n(n+1)}{2}$$

For \(n = 10\): \(\frac{10 \times 11}{2} = 55\). Same answer, zero looping.

In Python:

def closed_form(n: int) -> int:
    """Closed form for the sum of integers from 1 to n."""
    return n * (n + 1) // 2

print(closed_form(10))
55

The // is integer division — it divides and discards any remainder. Since either \(n\) or \(n+1\) is always even, \(n(n+1)\) is always divisible by 2, so // gives the exact answer here with no rounding.


Verifying That Both Approaches Agree

Whenever you have a closed form, it's good practice to verify it against the brute-force summation for small values. If they disagree anywhere, something is wrong.

print("n | summation | closed_form | match?")
print("-" * 40)
for n in range(1, 11):
    s = summation(lambda k: k, 1, n)
    c = closed_form(n)
    print(f"{n:2} | {s:9} | {c:11} | {s == c}")
n | summation | closed_form | match?
----------------------------------------
 1 |         1 |           1 | True
 2 |         3 |           3 | True
 3 |         6 |           6 | True
 4 |        10 |          10 | True
 5 |        15 |          15 | True
 6 |        21 |          21 | True
 7 |        28 |          28 | True
 8 |        36 |          36 | True
 9 |        45 |          45 | True
10 |        55 |          55 | True

Every row matches. This kind of verification table is a simple but powerful habit — it catches mistakes in either the formula or the code before they cause problems later.


Going Further — Summing Other Expressions

Because our summation function accepts any func, it works for any expression you can write as a Python function. For example, the sum of squares:

$$\sum_{k=1}^{n} k^2 = \frac{n(n+1)(2n+1)}{6}$$
# Brute-force version
sum_of_squares = summation(lambda k: k**2, 1, 5)
print(sum_of_squares)

# Closed form version
def closed_form_squares(n: int) -> int:
    """Closed form for the sum of squares from 1 to n."""
    return n * (n + 1) * (2 * n + 1) // 6

print(closed_form_squares(5))
55
55

Same result both ways. The closed form does it in one line of arithmetic; the summation function adds up \(1 + 4 + 9 + 16 + 25\) one at a time.


Why This Matters

Summations appear constantly in algorithm analysis. When you ask "how many steps does this loop take?", the answer is almost always a summation. Understanding how to compute them — and more importantly, how to replace a slow loop with a fast closed form — is one of the core skills in writing efficient code.

The general summation function we built here is also a useful debugging tool. Any time you derive a closed form mathematically, you can check it instantly against the brute-force version for small values before trusting it in production code.

If you want to strengthen your Python fundamentals for mathematical programming, you may also enjoy: Mastering Python Coding Practices: Learning from the Best.

Comments