For this first blog post, I picked the GCD as its topic because it was a rather basic topic that just about anyone who’s completed an introductory course to number theory should be familiar with, yet in my contest experience I’ve encountered some quite rich and interesting problems relating to GCDs which aren’t entirely trivial, even for those with experience in number theory.

If you already are comfortable with the idea of a greatest common divisor and the Euclidean algorithm, you can skip the first few sections and dive right into the two programming problems. This post is probably on the shorter side compared to other posts, but this is just me getting used to the site and all. Future posts will be substantially longer in both length of topic discussion and sample problems.

Unless explicitly stated otherwise, assume all letter variables in this blog post represent non-negative integers.

Problems Discussed:

What is the Greatest Common Divisor?

The greatest common divisor of two integers and , as I’m sure most of you know, if you’re the type to read this (Not a ) math blog, is the largest integer that divides both and . In symbols, we would write “ divides ” or, equivalently, “ is divisible by “, as . We would denote the GCD in symbols as , or even more simply, . I will be using the latter in this piece since we will be seeing it a lot.

The GCD is a commutative and associative operation, meaning that and . For this reason, we can get the GCD of an entire sequence of integers by applying the GCD to pairs of them one by one, just like we would take the sum of a set of numbers.

A lot of us probably remember the GCD from Grade School, where it would find itself usually in the form of questions such as “What are the largest square tiles that can be used to completely tile an rectangle?” Back then, we would have solved this by prime-factorizing and and encircling all the factors that they have in common. However, in practice, prime factorizing a large integer is an expensive and rather difficult endeavor, so we would like to avoid doing that if possible. Is there a way for us to find without having to find each’s factors?

The Euclidean Algorithm

Before we can learn this new technique, we will need to prove to ourselves some basic facts about the GCD.

Lemma 1.1

Proof. By definition, any integer divides 0, so we only need find the largest integer which divides , which is in fact itself.

Lemma 1.2

Proof. It is known that if and , then . Therefore if some integer were a common factor of and , it would also be a factor of . Also, if some integer were a common factor of and , then it should also be a common factor of . Therefore, all three quantities involved have the same common divisors, and thus must have the same greatest common divisor.

Lemma 1.3

Proof. This immediately follows from invoking the previously proven statement multiple times ( times, in fact).

Knowing these facts now allows us to use the fantastic Euclidean Algorithm, which I believe is most beautifully expressed in pseudo-code (actually Python):

def GCD(a,b): if b==0: return a else: return GCD(b,a%b)

Let’s dissect this. The modulo operator a%b is one you’ll see often in computer science, and basically just means “output the remainder when a is divided by b”. Remember how in Grade School, instead of saying , you’d say that ? Well, that means . Formally, we can uniquely write for some integer and some integer in the range (this is called the Division Algorithm), and .

This algorithm relies on the fact that , which shouldn't be too hard to see. Let , then , and we proved earlier that . Meaning, if we replace one number with the remainder when it is divided by the other number, the GCD will still be the same! This is desirable because the modulo operator makes the numbers shrink quite quickly, until we hit our base case of , which will always be equal to by Lemma 1.1.

In fact, the Euclidean Algorithm can be proven to run in logarithmic time. Oftentimes it is much faster than that; its worst case occurs in the case of asking for the GCD of two adjacent Fibonacci numbers, and I will leave that to you to think about.

Let's try the Euclidean Algorithm. What is the GCD of 144 and 40? Well, , so it should be the same as the GCD of 40 and 24. Since , this is the same as the GCD of 24 and 16, and since , this should be the same as the GCD of 16 and 8. Now, note that , so the GCD should be the same as the GCD of 0 and 8. However, the GCD of any number with 0 is the number itself, so the answer is 8. You can verify for yourself that 8 is the largest number that divides both 144 and 40.

Isn't that amazing! We were able to find the GCD of two numbers extremely efficiently without even knowing their prime factors at all.

Programming Problem 1 – Fibonacci GCD

This programming problem comes from the 9th Ad Infinitum, a Hackerrank contest “entirely in the mathematics domain”. It asks us to find the GCD of a set of Fibonacci numbers. Here, can be as large as , while the Fibonacci numbers themselves could be up to the th one. Since the answer can be quite large, the problem asks us to output them modulo . Recall that the Fibonacci numbers satisfy the recurrence relation with the base cases of and

This modulo actually turns out to be a problem for us, since the GCD isn’t necessarily preserved when modulo is applied to both arguments. Compare to . The former obviously has a GCD of 1, but when we look at the numbers , the latter suddenly have their GCD become 3.

It turns out there’s a remarkable fact that we can apply here, and it’s that the Fibonacci numbers form a strong divisibility sequence! A sequence of integers is called a divisibility sequence if implies that . The sequence is then called a strong divisibility sequence if . Amazingly enough, Fibonacci numbers have this quality! Note that and . We can see that . Isn’t that cool!

In order to easily prove this fact, we need to prove some lemmas first.

Lemma 2.1 implies that .

Proof. If we use the ‘common prime factors’ definition of GCD, then it becomes obvious; and have no common prime factors, so will only depend on the prime factors that will have in common with . There is a way to prove this without relying on a definition of prime numbers, but for now this proof will do for its brevity.

Lemma 2.2 Adjacent Fibonacci numbers are coprime, i.e. for all .

Proof. We can prove this by induction on . Let the base case be when . Then, , which is what we want. Now, assume that . Then, by definition of the Fibonacci numbers, Lemma 1.2, and the inductive hypothesis, respectively, .

Lemma 2.3

Proof. This may seem like a strange and out-of-nowhere formula to present, but it is quite pivotal to the proof; in fact, I would call it the element that shoulders the majority of the proving burden. The main motivation for why we would even want this formula is that it provides a way for us decompose into an expression in terms of and .

This can be proven by fixing to be a constant and performing induction on , and will be left as an exercise to the readers.

Sometime in the future, I plan to post two different proofs of this fact, using matrices and combinatorics respectively, that provide a far better intuition for this fact and, in my opinion, are far more beautiful proofs than merely proving it by induction.

Lemma 2.4 implies that .

Proof. First we have to show that the Fibonacci numbers form a divisibility sequence. We can reformulate this question to showing that . Now, we can prove this by induction on . For our base case, let , and it is definitely true that . Now, let us assume that . We need to show that . We apply Lemma 2.3 to get





We know that the left addend is divisible by , because is multiplied to it; we also know that the right addend is divisible by , because is multiplied to it, which we assumed to be divisible by by the inductive hypothesis. Thus, divides their sum, .

We now have the tools we need to prove that the Fibonacci numbers form a strong divisibility sequence.

Theorem 2.2

Proof. Assume, WLOG (without loss of generality), that , and thus, like earlier, we can write as . Thus,





Now, note that is a multiple of , by Theorem 2.1, so we can invoke Lemma 1.3 to say that

Now, we notice something: , by Lemma 2.2. Also, since by Lemma 2.4, that must mean that . If and had a common factor greater than 1, then it would also have been a common factor of and . That means we can use Lemma 2.1 and say that





Do you see that? It’s exactly the behavior of the Euclidean Algorithm, except it’s in the subscripts! Therefore, knowing how the Euclidean Algorithm works, we know it will terminate at





.

We can finally apply Lemma 1.1 again and .

Thus, ! This marvelous proof was taken from the book Proofs that Really Count: The Art of Combinatorial Proof by Benjamin and Quinn, which I highly recommend.

Therefore, to solve the problem you just need to get the GCD of all the indices first, then the answer would be th Fibonacci number .

How do you compute the th Fibonacci number in seconds, though? I will cover that in the next blog post! As a hint, it’s got a lot of fun with matrices.

Programming Problem 2 – Another Packing Problem

This programming problem comes from the Philippines’ National Olympiad for Informatics 2017. Given three positive integers , , and , which can each be up to 1 billion in size, the problem asks two values from us: the GCD of and , and the GCD of and . Furthermore, it asks to answer questions of this type up to 100,000 times!

The latter problem is incredibly simple, as it very obviously is just . The former question is far more interesting, though, and I shall spend the bulk of this piece answering it.

Note also that since and can get quite large, the program merely asks us to output the last 9 digits of the answer, i.e. to find the answer modulo .

Usually in problems like this, we can solve it by finding some way to evoke Euclidean-like behavior in the structure of the problem, similar to what we were able to do with Fibonacci numbers. WLOG, assume that $latex B







Note that (why?) so we can invoke Lemma 2.1 and say that







For now, let’s assume that . Thus, invoking Lemma 2.1 again,





Now this looks promising! We were able to prove that . This is remarkably similar to the Lemma that formed the basis of our Euclidean algorithm, and just like with the Fibonacci numbers, we were able to evoke it while still preserving the form of the particular function (which in this case is ). Thus, we have found that

We need to handle how it recurses into its smaller cases. Let . If , then we can just recursively call our GCD function on and , just like how we did for the Euclidean Algorithm. However if , we would get stuck in an infinite loop, since would still be . Instead, we just repeat our steps from earlier, one last time, yielding













Earlier, when we reached this step, we assumed that . However, now we know that , so we should factor out instead.





Which just about finishes our recurrence! Now, all we have to handle is the base cases. There are two possible states we could end at. One would be the standard Euclidean algorithm base case, which is that we get an of 0. In this case, we would need to find for some final value . Easily, it’s just 2 if is odd, and if is even.

However, there’s actually one more possibility, which is that we end up with for some iteration of the function. Note that if we tried running the algorithm on it now, it would result in an infinite loop. Handling this case however is easy, because if the two numbers are equal, then the GCD is just itself, (or equivalently ).

Thus we end with the function

def newGCD(A,B,C): if B==0: return 2 if A%2==1 else 1 elif B==C: return pow(A,B,MOD) r = C%(2*B) if r < B: return newGCD(r,B) else: return newGCD(2*B-r,B)

This function allows us to find modulo a large number in logarithmic time! If you feel like submitting to the practice page, I will leave hashing out the rest of the details to you 🙂

If you have Python, then pow(A,B,MOD) should be built in, but if not, how can you compute efficiently, especially since can be up to in size? Surprisingly, it’s actually similar to how we would find the th Fibonacci number efficiently!

Final Thoughts

Next blog post will complete the above solutions by discussing a method by which we can find and more efficiently, called Fast Exponentiation. It will start to get heavier on the programming side, but don’t worry, since there’s still a lot of Math to be discussed! I’ll also probably discuss 3 to 4 problems instead of just 2. All feedback is appreciated, and I’ll see you next time!