Skip to main content

Why Doesn’t The Simple Formula Work?

Problem: Write a program to find sum of all multiples of 3 and 5 upto input n. If a number is multiple of both 3 and 5 add it only once.

We were given this problem at work. I tried to “solve” it using math. Clearly, we can add all the multiples of 3 and 5 upto n and subtract multiples of 15 once to account for the overcounting. We could derive formula for this as follows:

$$\begin{aligned} f(n) &= (3 + 6 + 9 + \ldots) + (5 + 10 + 15 + \ldots) - (15 + 30 + 45 + \ldots) \newline &= 3(1 + 2 + 3 + \ldots) + 5(1 + 2 + 3 + \ldots) - 15 (1 + 2 + 3 + \ldots) \newline &= 3S(n/3) + 5S(n/5) - 15S(n/15) \text{, where } S(k) := \frac{1}{2} k (k + 1) \newline &= 3 \times \frac{1}{2} \frac{n}{3} \left( \frac{n}{3} + 1\right) + 5 \times \frac{1}{2} \frac{n}{5} \left( \frac{n}{5} + 1\right) - 15 \times \frac{1}{2} \frac{n}{15} \left( \frac{n}{3} + 1\right) \newline &= \frac{n}{2} \left( \frac{n}{3} + 1 + \frac{n}{5} + 1 - \frac{n}{15} - 1 \right) \newline &= \frac{n}{2} \left( \frac{n}{3} +\frac{n}{5} - \frac{n}{15} + 1 \right) \end{aligned}$$

We write code to do the calculations for us like this:

def s(n):
    return (n * (n + 1)) // 2

def ss(n):
    return 3 * s(n//3) + 5 * s(n//5) - 15 * s(n//15)

def sss(n):
    return (n // 2) * ( n // 3 + n // 5 - n // 15 + 1)

tests = [(3, 3), (5,8),(0,0), (10,23+10), (1000, 233168+1000)]

for (n, expected) in tests:
    print(n, expected, ss(n), sss(n))

# print(n, expected, ss(n))
assert ss(n) == expected

# Outputs:
# 3 3 3 2
# 5 8 8 6
# 0 0 0 0
# 10 33 33 30
# 1000 234168 234168 234000

Can you figure out why ss gives the right answer but sss doesn’t? Math is straightforward isn’t it? Enjoy!