0% found this document useful (0 votes)
38 views4 pages

INCA01 Binaryexponentiation

The document discusses the problem of modular exponentiation, specifically calculating 3^N mod M for large values of N and M. It presents a straightforward algorithm that fails for large N due to integer overflow, and then introduces an optimized approach using modular arithmetic and binary exponentiation to achieve a time complexity of O(log N). Additionally, it lists related problems and resources for further reading on the topic.

Uploaded by

Raouf Ouldali
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views4 pages

INCA01 Binaryexponentiation

The document discusses the problem of modular exponentiation, specifically calculating 3^N mod M for large values of N and M. It presents a straightforward algorithm that fails for large N due to integer overflow, and then introduces an optimized approach using modular arithmetic and binary exponentiation to achieve a time complexity of O(log N). Additionally, it lists related problems and resources for further reading on the topic.

Uploaded by

Raouf Ouldali
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Raouf Ould Ali - Algerian Olympiad in Informatics

The Incarnadine Training - Incarnadine I

Binary Exponentiation
Lesson & Problems

© Raouf Ould Ali - Algerian Olympiad in Informatics 1


The Incarnadine Training Binary Exponentiation

Problem: Modular Exponentiation 💡


Consider the following problem:

• Write a program that outputs the remainder of the Euclidean division of 3N by M , where
1 ≤ N ≤ 1018 and 1 ≤ M ≤ 109 .

Straightforward Algorithm 📏
The straightforward algorithm would be the following:
#include <bits/stdc++.h>
using namespace std;
int main(){
long long N;
int M;
cin >> N >> M;

long long result = 1;


for(int i = 0; i < N; i++){
result = result * 3;
}

cout << result % M << endl;


}

After trying N = 10, M = 2000, for example, we may think that we’re done with the problem.
But when trying with some higher values, we start getting some nonsense values. For instance,
with N = 100, M = 32000, we get −9263, which doesn’t make sense at all! This has a very simple
explanation: as 3N becomes larger and larger, it exceeds the maximum integer C++ can store in
memory. Do you have any idea about how to solve this issue?

Modular Arithmetic to the Rescue 🦸


Remember that we’re not searching for the exact value, but only its remainder when divided by
M . Hence, we can use the fact that:

ab mod M = (a mod M )(b mod M )

This allows us to perform the computations by replacing at each step the variable result by its
division remainder by M :

© Raouf Ould Ali - Algerian Olympiad in Informatics 2


The Incarnadine Training Binary Exponentiation

#include <bits/stdc++.h>
using namespace std;
int main(){
long long N;
int M;
cin >> N >> M;

long long result = 1;


for(int i = 0; i < N; i++){
result = result * 3;
result = result % M;
}

cout << result << endl;


}

Retrying N = 100, M = 32000 gives us the intended solution (2001). Let’s goooo, we solved it!
🥳

Optimizing for Large N 🚀


Remember that the constraints say that N ’s upper bound is 1018 , but our algorithm obviously runs
in O(N ), which is too slow (You can notice it yourself trying N = 100000000000, M = 237564).
Can you figure out a solution?
Instead of multiplying N times the result variable by 3, you can notice the following facts:
32k = 3k × 3k
32k+1 = 3k × 3k × 3
This allows us to perform our calculation in O(log N ) instead of O(N ), as to compute 32k , we only
need to compute 3k once and just square it, as shown in the algorithm below:
#include <bits/stdc++.h>
using namespace std;

long long ThreePow(long long k, int M){


if(k == 0) return 1;
long long subRes = ThreePow(k / 2, M); // If k is odd, k/2 computes its floor, aka
(2j+1)/2 = j
if(k % 2) // k is odd
return (((subRes * subRes) % M) * 3) % M;
else // k is even
return (subRes * subRes) % M;
}

int main(){
long long N;
int M;
cin >> N >> M;
cout << ThreePow(N, M) << endl;
}

© Raouf Ould Ali - Algerian Olympiad in Informatics 3


The Incarnadine Training Binary Exponentiation

As we’re dividing each time k by 2, it is obvious that the number of calls to our function is
equal to blog2 N c + 2, giving a time complexity of O(log N ). (You can try some test cases by hand
on a piece of paper to figure it out.)

Your Favorite Section: Problems 📚


Time for problems; 50 points each!

• UVA 374 - Big Mod

• LeetCode 1922 - Count Good Numbers

• Codeforces 630I - Again Twenty Five!

• Codeforces Group Problem A (made by me 😉)

If You Wanna Read More 📖


These articles give further explanations but also more advanced examples:

• Binary Exponentiation - CP Algorithms

• Binary Exponentiation for Competitive Programming - GeeksforGeeks

© Raouf Ould Ali - Algerian Olympiad in Informatics 4

You might also like