Syllabus Lesson 133 of 156 · Privacy tech beyond blocking
Privacy tech beyond blocking

The maths you need

Why it matters The privacy tools in this module, such as finding shared customers without swapping lists or publishing statistics that protect each person, rest on three small pieces of maths. You do not need a maths degree. You need clock arithmetic, a feel for chance, and the idea that random noise cancels out in large groups. This lesson gives you exactly those, with Python to try.

1. Clock arithmetic

It is 22:00. What time is it 5 hours later? Not 27:00: the clock wraps round, so it is 03:00. You took 22 + 5 = 27 and kept the remainder after dividing by 24.

Modular arithmetic: arithmetic where numbers wrap round after reaching a fixed size, the modulus. "27 mod 24" is 3, the remainder after dividing 27 by 24. In Python the remainder operator is %: 27 % 24 is 3.

Wrapping keeps numbers a fixed size however much you add or multiply, which is what makes it useful for cryptography. Python also has a fast built-in for "raise to a power, then wrap": pow(base, exponent, modulus).

print((22 + 5) % 24)       # 3
print(pow(3, 4, 7))        # 3*3*3*3 = 81, and 81 % 7 = 4
print(pow(5, 200, 23))     # instant, even though 5 to the 200 is huge

2. Easy one way, hard the other

Mixing yellow and blue paint into green is easy. Unmixing green back into the exact yellow and blue is practically impossible. Some maths works the same way.

Pick a large prime modulus P and a number h. Computing pow(h, a, P) for a secret a is instant. But given only the answer, finding a again is so slow for big enough numbers that nobody can do it. This one-way property is called the discrete logarithm problem.

One more fact, and it powers the next labs. Raising to a then to b gives the same result as raising to b then to a, because both equal h to the power a times b:

P = 1019                     # a small prime: fine for learning, useless for security
h, a, b = 42, 123, 456
print(pow(pow(h, a, P), b, P) == pow(pow(h, b, P), a, P))    # True

Like two locks on one box, the order you put them on does not matter. Two people can each add their own secret and end up at the same place without ever revealing their secrets.

3. Probability in one paragraph

A probability is a number from 0 (never) to 1 (always). A fair coin lands heads with probability 0.5. When two things are independent, the chance of both is the product: two heads in a row is 0.5 x 0.5 = 0.25. The expected value is the long-run average: roll a fair die many times and the average approaches 3.5.

4. Noise cancels out in a crowd

Noise: a random amount added to a true value on purpose, so the exact value is hidden. If the noise is as likely to be up as down, it averages out to zero over many values.

Here is a classic survey trick called randomised response. You want to know how many people have done something embarrassing. Each person secretly flips a coin. Heads: answer truthfully. Tails: flip again and answer "yes" for heads and "no" for tails. Any single "yes" could just be the coin, so nobody's answer proves anything about them.

Yet the crowd's total is still useful. If the true fraction is p, the fraction of "yes" answers you expect is 0.5 x p + 0.25. So if 35% say yes, p is about (0.35 - 0.25) / 0.5 = 0.2, or 20%. The bigger the crowd, the closer the estimate, because random noise shrinks relative to the total as the crowd grows.

import random
rng = random.Random(1)               # seeded: the same "random" numbers every run
truth = [rng.random() < 0.2 for _ in range(10000)]   # 20% really did it
answers = []
for did_it in truth:
    if rng.random() < 0.5:
        answers.append(did_it)             # heads: tell the truth
    else:
        answers.append(rng.random() < 0.5) # tails: random yes or no
yes = sum(answers) / len(answers)
print(round((yes - 0.25) / 0.5, 3))       # close to 0.2

The noise used in differential privacy, called Laplace noise, is a smooth version of the same idea: usually small, occasionally larger, equally likely up or down. Its scale sets how big it typically is.

In other languages

JavaScript's % works the same way for positive numbers, and big numbers use BigInt (written with an n). There is no built-in modular power, so here is the standard loop:

function powMod(base, exp, mod) {
  let result = 1n;
  base %= mod;
  while (exp > 0n) {
    if (exp & 1n) result = (result * base) % mod;
    base = (base * base) % mod;
    exp >>= 1n;
  }
  return result;
}
console.log(powMod(3n, 4n, 7n));   // 4n

In Go, new(big.Int).Exp(base, exp, mod) from math/big does the same job.

A common misconception

"Adding noise makes the data useless." For one person's value, yes, and that is the point. For a large group, the noise largely cancels, so totals and averages stay useful. Privacy tech lives in that gap: blurry for individuals, sharp for crowds.

Recap

  • Modular arithmetic wraps numbers round a modulus; Python uses % and pow(b, e, m).
  • Raising to a secret power is easy one way and practically impossible to undo, and the order of two secret powers does not matter.
  • Independent chances multiply; the expected value is the long-run average.
  • Random noise hides individuals but cancels out in large groups, which is how randomised response works.

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output