pwshub.com

Binary Exponentiation Algorithm – Explained with Practical Examples

Binary Exponentiation Algorithm – Explained with Practical Examples

Binary exponentiation, also known as exponentiation by squaring, is a powerful algorithm used to efficiently calculate large powers of numbers. This technique is particularly useful in various fields of computer science, including cryptography, competitive programming, and computer graphics.

In this article, we'll explore the concept of binary exponentiation, understand how it works, and implement it in code.

Binary exponentiation is a method to compute \(a^n\) (a raised to the power of n) using only multiplications, instead of the naïve \(O(n)\) multiplications.

This significant improvement in efficiency makes it possible to calculate extremely large powers quickly, even when dealing with modular arithmetic.

How Binary Exponentiation Works

The key idea behind binary exponentiation is to break down the exponent into its binary representation and use the properties of exponents to simplify the calculation.

Let's break it down step by step:

  1. Convert the exponent n to its binary representation.

  2. Initialize the result as 1 and the base as a.

  3. Iterate through each bit of the binary representation of n from right to left: (a). If the current bit is 1, multiply the result by the current base. (b). Square the base (multiply it by itself).

  4. Return the final result.

For example, let's calculate \(3^{13}\):

  1. Convert 13 to binary: \(13_{10} = 1101_2\)

  2. Initialize result = 1, base = 3

  3. Iterate through the bits:

    • Bit 1: result = \(1 *3 = 3\), base = \(3 *3 = 9\)

    • Bit 0: result = 3, base = \(9 * 9 = 81\)

    • Bit 1: result = \(3 *81 = 243\), base = \(81 *81 = 6561\)

    • Bit 1: result = \(243 * 6561 = 1,594,323\)

Thus, \(3^{13}=1,594,323.\)

Why Binary Exponentiation is Efficient

The efficiency of binary exponentiation comes from two main factors:

  1. Reduced number of multiplications: Instead of performing n-1 multiplications as in the naïve approach, we only perform \(O(log n)\) multiplications. This is because we're essentially breaking down the problem into smaller subproblems based on the binary representation of the exponent.

  2. Reuse of previous calculations: By squaring the base at each step, we're reusing the results of previous calculations, which significantly reduces the overall number of operations needed.

To illustrate this efficiency, consider calculating \(a^{1000000}\). The naïve approach would require 999,999 multiplications, while binary exponentiation would only require about 20 multiplications (as \(\log_2(1000000) \approx 20\)).

Algorithm Implementation

Let's implement the binary exponentiation algorithm in Python:

def binary_exponentiation(base, exponent):
    result = 1
    while exponent > 0:
        # If the current bit is 1, multiply the result by the current base
        if exponent & 1:
            result *= base
        # Square the base
        base *= base
        # Move to the next bit
        exponent >>= 1
    return result
# Example usage
print(binary_exponentiation(3, 13))  # Output: 1594323

Let's break down the algorithm:

  1. We initialize result to 1, which is the identity for multiplication.

  2. We use a while loop to iterate until the exponent becomes 0.

  3. We check if the least significant bit of the exponent is 1 using the bitwise AND operator &. If it is, we multiply the result by the current base.

  4. We square the base by multiplying it by itself.

  5. We use the right shift operator >>= to move to the next bit of the exponent.

  6. Finally, we return the result.

Time Complexity Analysis

The time complexity of binary exponentiation is \(O(log n)\), where n is the exponent. This is because:

  1. The number of bits in the binary representation of n is \(\lfloor \log_2 n\rfloor + 1\).

  2. We perform at most two multiplications per bit (one for squaring the base, and potentially one for updating the result).

Therefore, the total number of multiplications is at most \(2(\lfloor \log_2 n \rfloor + 1)\), which simplifies to \(O(\log n)\).

Example Problems and Solutions

Let's look at some algorithmic problems that you can solve efficiently using binary exponentiation, along with detailed explanations of the solutions and how we arrived at using binary exponentiation.

Problem 1: Modular Exponentiation

Problem: Calculate \(3^{1000000} \bmod 1000000007\).

Approach:

  1. We recognize that this problem involves a very large exponent (1000000), which would be impractical to compute using naïve exponentiation.

  2. We also notice that we need to find the result modulo a large prime number (1000000007).

  3. This combination of a large exponent and modular arithmetic is a clear indicator that we should use modular binary exponentiation.

Solution: We'll modify our binary exponentiation function to include modular arithmetic:

def mod_binary_exponentiation(base, exponent, mod):
    result = 1
    base %= mod
    while exponent > 0:
        if exponent & 1:
            result = (result * base) % mod
        base = (base * base) % mod
        exponent >>= 1
    return result
print(mod_binary_exponentiation(3, 1000000, 1000000007))  # Output: 624098969

Explanation:

  1. We initialize result to 1 and set base to base % mod to handle cases where the initial base is larger than the modulus.

  2. The main loop works similarly to the original binary exponentiation algorithm, but with two key differences:

    a. When updating result, we perform (result * base) % mod. This ensures that result never exceeds mod, preventing integer overflow and maintaining the correct modular result.

    b. When squaring base, we perform (base * base) % mod for the same reason.

  3. The bitwise operations (exponent & 1 and exponent >>= 1) work exactly as in the original algorithm, allowing us to process the binary representation of the exponent efficiently.

  4. By applying the modulo operation at each step, we ensure that all intermediate results remain within the range [0, mod-1]. This is possible because of the properties of modular arithmetic:

    $$(a⋅b)modm=((amodm)⋅(bmodm))modm$$

This problem would be impossible to solve with naïve exponentiation due to the huge result, but modular binary exponentiation makes it tractable by keeping all intermediate results manageable.

Problem 2: Matrix Exponentiation

Problem: Given a 2x2 matrix A, calculate An where n = 1000000.

Approach:

  1. We observe that we need to raise a matrix to a very large power (1000000).

  2. Matrix multiplication is associative, which allows us to use the same principle as binary exponentiation for numbers.

  3. We recognize that this is a perfect scenario for applying binary exponentiation to matrices.

Solution: We can use binary exponentiation on matrices. Here's a Python implementation with explanations:

import numpy as np
def matrix_multiply(A, B, mod):
    return np.array([[(A[0][0]*B[0][0] + A[0][1]*B[1][0]) % mod, (A[0][0]*B[0][1] + A[0][1]*B[1][1]) % mod],
                     [(A[1][0]*B[0][0] + A[1][1]*B[1][0]) % mod, (A[1][0]*B[0][1] + A[1][1]*B[1][1]) % mod]])
def matrix_power(A, n, mod):
    result = np.eye(2, dtype=int)
    while n > 0:
        if n & 1:
            result = matrix_multiply(result, A, mod)
        A = matrix_multiply(A, A, mod)
        n >>= 1
    return result
A = np.array([[1, 1], [1, 0]])
n = 1000000
mod = 1000000007
result = matrix_power(A, n, mod)
print(result)  # Output: [[690749268 297612485]
                #         [297612485 393136783]]

Explanation:

  1. matrix_multiply(A, B, mod):

    • This function performs matrix multiplication of two 2x2 matrices, A and B.

    • Each element of the resulting matrix is computed using the standard matrix multiplication formula, followed by a modulo operation to keep the values manageable.

  2. matrix_power(A, n, mod):

    • This function implements binary exponentiation for matrices.

    • We start with result as the 2x2 identity matrix (created using np.eye(2, dtype=int)).

    • The main loop follows the same pattern as scalar binary exponentiation: a. If the current bit of n is 1, we multiply result by the current A. b. We square A (by multiplying it with itself). c. We right-shift n to move to the next bit.

    • All matrix multiplications are done using our matrix_multiply function, which incorporates modular arithmetic.

This matrix exponentiation technique is particularly powerful for solving linear recurrence relations in logarithmic time, as demonstrated here with the Fibonacci sequence.

Practice Problems

Here are some problems for you to solve using binary exponentiation:

  1. Modular Exponentiation: Calculate \(7^{1234567} \bmod 1000000009\). (Hint: Use the mod_binary_exponentiation function from Problem 1 in the examples.)

  2. Last Digit: Find the last digit of . (Hint: Observe the pattern of last digits of powers of 2 and use binary exponentiation with modulo 10.)

  3. Power Tower: Calculate the last 3 digits of \(2^{2^{20}}\). (Hint: Use the property of modular arithmetic that \(a^b \bmod m = a^{b \bmod \phi(m)} \bmod m\) where \(\phi\) is Euler's totient function. You'll need to calculate \(2^{20} \bmod \phi(1000)\) first.)

  4. Matrix Chains: Given a 2x2 matrix A = [[1, 2], [3, 4]], calculate the last two digits of the sum of all elements in \(A^{1000000}\). (Hint: Use matrix exponentiation as in Problem 2 from the examples, but only keep track of the last two digits of each element. You'll need to sum the elements after the final exponentiation.)

  5. Fibonacci Sequence: Find the 1000000th Fibonacci number modulo 1000000007. (Hint: Use the matrix form of the Fibonacci sequence ([[1, 1], [1, 0]]) and apply matrix exponentiation as shown in Problem 2 of the examples.)

Conclusion

Binary exponentiation is a powerful technique that can be applied to a wide range of problems involving large exponents. As we've seen in the example and practice problems, it's particularly useful in modular arithmetic, matrix operations, and solving recurrence relations.

By practicing these problems, you'll gain a deeper understanding of how to apply binary exponentiation in various scenarios. Remember, the key is to recognize when a problem involves raising something to a large power, whether it's a number, a matrix, or even a more complex structure.

If you found this explanation of Binary Exponentiation algorithm helpful, you might also enjoy more in-depth programming tutorials and concepts I cover on my blog.

Source: freecodecamp.org

Related stories
1 month ago - One of the biggest strengths of Go is its compiler. It abstracts many things for you and lets you compile your program easily for almost any platform and architecture. And though it seems easy, there are some nuances to it and multiple...
1 month ago - WebAuthn on Chrome can now use hints, Related Origin Requests and JSON serialization
1 month ago - Mozilla Firefox 130 is out with a variety of changes that make this phenomenally popular open-source web browser a touch more productive. On Linux, Firefox 130 enables overscroll animations by default, having added them on other platforms...
1 month ago - What is pip? In this beginner-friendly tutorial, you'll learn how to use pip, the standard package manager for Python, so that you can install and manage packages that aren't part of the Python standard library.
1 month ago - Secure your mission-critical data with S3 Express One Zone's server-side encryption using KMS keys, combining top-notch performance and robust security for regulatory compliance.
Other stories
38 minutes ago - Fixes 41 bugs (addressing 595 👍). node:http2 server and gRPC server support, ca and cafile support in bun install, Bun.inspect.table, bun build --drop, iterable SQLite queries, iterator helpers, Promise.try, Buffer.copyBytesFrom, and...
4 hours ago - This guide provides a foundational understanding of Redux and why you should use it for state management in a React app. The post Understanding Redux: A tutorial with examples appeared first on LogRocket Blog.
7 hours ago - Discover some of the best Node.js web scraping libraries, including Axios and Superagent, and techniques for how to use them. The post The best Node.js web scrapers for your use case appeared first on LogRocket Blog.
10 hours ago - Infinite runner games have been a favorite for gamers and developers alike due to their fast-paced action and replayability. These games often feature engaging mechanics like endless levels, smooth character movement, and dynamic...
12 hours ago - Yesterday, Elizabeth Siegle, a developer advocate for CLoudflare, showed off a really freaking cool demo making use of Cloudflare's Workers AI support. Her demo made use of WNBA stats to create a beautiful dashboard that's then enhanced...