No history yet

Modular Inverse

Transcript

Beau

Okay, so we've covered modular addition, subtraction, and multiplication. It's actually pretty intuitive, right? You just... do the operation, take the modulo at each step to keep the numbers small. Super handy for avoiding overflow with long longs in C++.

Jo

Exactly. The properties hold up really nicely. It's like working in a completely self-contained little universe of numbers defined by your modulus.

Beau

Right. But... I was messing around with a problem and I naturally just tried to do division the same way. You know, like `(a / b) % m`. And the results were just... garbage. Completely wrong. So what gives? Why does division get left out of the party?

Jo

That is the million-dollar question. And you're right, it doesn't work. Division isn't distributive over the modulo operator like the others. Think about it with a simple case: `(10 / 2) % 6`. The real answer is 5. But if we try to apply the modulo first, we get `(10 % 6) / (2 % 6)`, which is `4 / 2`, which is 2. Totally different numbers.

Beau

Okay, yeah, that makes sense. It breaks the whole system of congruency we just set up. So... are we just stuck? We can't do division in modular arithmetic?

Jo

Not stuck, we just have to be more clever. Instead of dividing, we multiply by a special number. Think about regular arithmetic. Dividing by 5 is the same as multiplying by `1/5`, or `5` to the power of negative one.

Beau

The multiplicative inverse. Right.

Jo

Exactly. We need to find the modular equivalent of that. We need to find a number, let's call it b-inverse, such that `(b * b-inverse)` is congruent to `1 (mod m)`. If we can find that number, then dividing `a` by `b` becomes multiplying `a` by `b-inverse`.

Beau

So `(a / b) % m` becomes `(a * b_inverse) % m`. And that works because we already know how to do modular multiplication. That's clever. But how do we find this... magic b-inverse number?

Jo

Well, first, there's a catch. This modular multiplicative inverse, or MMI, doesn't always exist. It only exists if the number you're 'dividing' by, `b`, and the modulus, `m`, are coprime.

Beau

Coprime... meaning their greatest common divisor is 1. Okay. So `gcd(b, m)` has to be 1. Why is that?

Jo

It falls out of how we find it. The tool we use is called the Extended Euclidean Algorithm. You know the standard Euclidean algorithm for finding the GCD of two numbers, right?

Beau

Yeah, the one where you keep taking `a % b` until you hit zero.

Jo

That's the one. The extended version does that, but it also finds two integers, let's call them `x` and `y`, that satisfy a cool property: `b*x + m*y = gcd(b, m)`. This is known as Bézout's identity.

Beau

Okay, `bx + my = gcd(b, m)`. I feel like we're getting deep into the math here, but... how does that give us the inverse?

Jo

This is the 'aha' moment. Remember the condition for the inverse to exist? `gcd(b, m)` must be 1.

Beau

So the equation becomes `bx + my = 1`.

Jo

Exactly. Now, what happens if we take that whole equation modulo `m`?

Beau

Umm... `(bx + my) % m` is congruent to `1 % m`. The `m*y` term... well, `m*y` is a multiple of `m`, so `(m*y) % m` is just zero.

Jo

Perfect. So the `m*y` term vanishes.

Beau

And we're left with... `bx` is congruent to `1 (mod m)`. Whoa. Okay. That's the definition of the modular inverse we started with. So... `x` is our answer. The `x` from the extended euclidean algorithm is the modular multiplicative inverse of `b`.

Jo

There it is. That's the whole connection. The algorithm is specifically designed to find the coefficients that make that identity true, and when the gcd is 1, one of those coefficients happens to be exactly the number we're looking for.

Beau

So in practice, for a competitive programming problem, if I need to compute `(a / b) % m`, my first step is to check if `gcd(b, m) == 1`. If not, I'm out of luck with this method. If it is, I run an extended Euclidean algorithm function with `b` and `m`, which gives me back `x` and `y`. And my inverse is `x`.

Jo

Almost. One tiny implementation detail. The `x` that the algorithm returns can be negative. But we want our answer to be a valid residue class, so it should be between 0 and `m-1`. If `x` is negative, you just add `m` to it to bring it into the positive range.

Beau

Ah, the classic negative modulo issue again. So the final inverse is `(x % m + m) % m`. Got it. Then I just compute `(a * inverse) % m` and I have my answer.

Jo

You got it. The Extended Euclidean Algorithm typically runs in O(log m) time, which is very efficient. So this entire process is fast enough for pretty much any competitive programming context.

Beau

That's amazing. So we've basically traded a forbidden operation, division, for one that we're perfectly allowed to do—multiplication—by finding this special key. It's like a mathematical loophole.