Facebook Pixel

50. Pow(x, n)

Problem Description

You need to implement a function that calculates x raised to the power n (written as x^n). This is the mathematical operation of exponentiation where you multiply x by itself n times.

For example:

  • pow(2, 10) should return 1024 (2 multiplied by itself 10 times)
  • pow(2.1, 3) should return 9.261 (2.1 × 2.1 × 2.1)
  • pow(2, -2) should return 0.25 (which is 1/(2×2) = 1/4)

The function should handle:

  • Any floating-point number x as the base
  • Any integer n as the exponent (including negative numbers and zero)
  • When n is negative, the result should be 1/x^|n|
  • When n is zero, the result should be 1 (any number to the power of 0 equals 1)

The solution uses a fast powering technique (also called binary exponentiation) to efficiently compute the result. Instead of multiplying x by itself n times which would take O(n) time, this algorithm works by breaking down the exponent into its binary representation and uses the property that x^(a+b) = x^a × x^b.

The algorithm repeatedly:

  1. Checks if the current bit of n is 1 (using n & 1)
  2. If yes, multiplies the answer by the current power of x
  3. Squares the current power of x (since each bit position represents a power of 2)
  4. Shifts n right by 1 bit to process the next bit

This reduces the time complexity from O(n) to O(log n), making it much more efficient for large exponents.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The naive approach to calculate x^n would be to multiply x by itself n times. But when n is very large like 1000000, this becomes extremely inefficient.

The key insight comes from observing how we can break down powers using the properties of exponents. Consider calculating x^10:

  • Instead of doing x × x × x × x × x × x × x × x × x × x (10 multiplications)
  • We notice that x^10 = x^8 × x^2 = (x^4)^2 × x^2

This leads us to think about the binary representation of the exponent. The number 10 in binary is 1010, which means:

  • 10 = 8 + 2 = 2^3 + 2^1
  • Therefore, x^10 = x^(8+2) = x^8 × x^2

We can compute these powers of 2 efficiently by repeatedly squaring:

  • x^1 = x
  • x^2 = x × x (square once)
  • x^4 = x^2 × x^2 (square again)
  • x^8 = x^4 × x^4 (square again)

So instead of 10 multiplications, we only need:

  • 3 squaring operations to get x^2, x^4, x^8
  • 1 multiplication to combine x^8 × x^2

This pattern works for any exponent. We can always express n as a sum of powers of 2 (its binary representation), compute those specific powers by repeated squaring, and multiply together only the ones we need.

The algorithm naturally falls out from this observation: we examine each bit of n from right to left, maintaining the current power of x (which doubles each time), and accumulate the result whenever we encounter a 1 bit.

For negative exponents, we use the mathematical property that x^(-n) = 1/(x^n), so we compute the positive power first and then take its reciprocal.

Learn more about Recursion and Math patterns.

Solution Approach

The solution implements the fast powering (binary exponentiation) algorithm to efficiently compute x^n in O(log n) time.

The implementation consists of two parts:

  1. Main function myPow: Handles the sign of the exponent

    • If n >= 0, directly calls the helper function qpow(x, n)
    • If n < 0, computes 1 / qpow(x, -n) to handle negative powers
  2. Helper function qpow: Performs the actual fast powering calculation

    The algorithm works as follows:

    • Initialize ans = 1 to store the result
    • While n is not zero:
      • Check if the least significant bit of n is 1 using n & 1
        • If yes, multiply ans by the current value of a: ans *= a
        • This corresponds to including this power of 2 in our result
      • Square a by doing a *= a
        • This prepares the next power of 2 (e.g., from x^2 to x^4)
      • Right shift n by 1 bit using n >>= 1
        • This moves to the next bit in the binary representation
    • Return the accumulated result ans

Example walkthrough for x = 2, n = 10 (binary: 1010):

  • Initially: ans = 1, a = 2, n = 10 (1010)
  • Iteration 1: n & 1 = 0, skip multiplication, a = 4, n = 5 (101)
  • Iteration 2: n & 1 = 1, ans = 1 × 4 = 4, a = 16, n = 2 (10)
  • Iteration 3: n & 1 = 0, skip multiplication, a = 256, n = 1 (1)
  • Iteration 4: n & 1 = 1, ans = 4 × 256 = 1024, a = 65536, n = 0
  • Result: 1024

The algorithm efficiently computes the result by only multiplying together the necessary powers of x based on the binary representation of n, achieving logarithmic time complexity instead of linear.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through calculating pow(3, 5) using the binary exponentiation approach.

Setup:

  • We want to compute 3^5
  • The number 5 in binary is 101, which means 5 = 4 + 1 = 2^2 + 2^0
  • Therefore, 3^5 = 3^4 × 3^1

Step-by-step execution:

Initial state:

  • ans = 1 (accumulator for result)
  • a = 3 (current power of x)
  • n = 5 (binary: 101)

Iteration 1:

  • Check rightmost bit: 5 & 1 = 1 (bit is 1)
  • Multiply answer: ans = 1 × 3 = 3
  • Square the base: a = 3 × 3 = 9 (now represents 3^2)
  • Shift right: n = 5 >> 1 = 2 (binary: 10)

Iteration 2:

  • Check rightmost bit: 2 & 1 = 0 (bit is 0)
  • Skip multiplication (don't need 3^2 in final result)
  • Square the base: a = 9 × 9 = 81 (now represents 3^4)
  • Shift right: n = 2 >> 1 = 1 (binary: 1)

Iteration 3:

  • Check rightmost bit: 1 & 1 = 1 (bit is 1)
  • Multiply answer: ans = 3 × 81 = 243
  • Square the base: a = 81 × 81 = 6561 (now represents 3^8)
  • Shift right: n = 1 >> 1 = 0

Loop ends (n = 0)

Result: ans = 243

We can verify: 3^5 = 3 × 3 × 3 × 3 × 3 = 243

Notice how we only needed 3 iterations (log₂(5) rounded up) instead of 5 multiplications. The algorithm selected exactly the powers we needed (3^1 and 3^4) based on the binary representation of 5.

Solution Implementation

1class Solution:
2    def myPow(self, x: float, n: int) -> float:
3        """
4        Calculate x raised to the power n (x^n).
5        Uses binary exponentiation for O(log n) time complexity.
6      
7        Args:
8            x: The base number
9            n: The exponent (can be negative)
10      
11        Returns:
12            The result of x^n
13        """
14      
15        def quick_power(base: float, exponent: int) -> float:
16            """
17            Helper function to calculate base^exponent using binary exponentiation.
18            Only handles non-negative exponents.
19          
20            Args:
21                base: The base number
22                exponent: Non-negative exponent
23          
24            Returns:
25                The result of base^exponent
26            """
27            result = 1.0
28          
29            # Process each bit of the exponent
30            while exponent > 0:
31                # If current bit is 1, multiply result by current base
32                if exponent & 1:  # Check if least significant bit is 1
33                    result *= base
34              
35                # Square the base for the next bit position
36                base *= base
37              
38                # Right shift to process the next bit
39                exponent >>= 1
40          
41            return result
42      
43        # Handle negative exponents by computing 1 / (x^(-n))
44        if n >= 0:
45            return quick_power(x, n)
46        else:
47            return 1.0 / quick_power(x, -n)
48
1class Solution {
2    /**
3     * Calculates x raised to the power n (x^n)
4     * @param x the base number
5     * @param n the exponent (can be negative)
6     * @return the result of x^n
7     */
8    public double myPow(double x, int n) {
9        // Handle positive and negative exponents separately
10        // For negative n, calculate x^(-n) and return its reciprocal
11        // Cast n to long to handle Integer.MIN_VALUE overflow when negating
12        return n >= 0 ? quickPower(x, n) : 1.0 / quickPower(x, -(long) n);
13    }
14
15    /**
16     * Fast exponentiation using binary exponentiation algorithm
17     * Time complexity: O(log n)
18     * @param base the base number to be raised to a power
19     * @param exponent the non-negative exponent
20     * @return the result of base^exponent
21     */
22    private double quickPower(double base, long exponent) {
23        double result = 1.0;
24      
25        // Binary exponentiation: process exponent bit by bit
26        while (exponent > 0) {
27            // If current bit is 1, multiply result by current base
28            if ((exponent & 1) == 1) {
29                result = result * base;
30            }
31            // Square the base for the next bit position
32            base = base * base;
33            // Right shift to process the next bit
34            exponent >>= 1;
35        }
36      
37        return result;
38    }
39}
40
1class Solution {
2public:
3    double myPow(double x, int n) {
4        // Lambda function for fast exponentiation using binary exponentiation
5        auto fastPower = [](double base, long long exponent) {
6            double result = 1.0;
7          
8            // Iterate while exponent is not zero
9            while (exponent > 0) {
10                // If current bit is 1, multiply result by current base
11                if (exponent & 1) {
12                    result *= base;
13                }
14                // Square the base for next bit position
15                base *= base;
16                // Right shift to process next bit
17                exponent >>= 1;
18            }
19          
20            return result;
21        };
22      
23        // Handle positive and negative exponents
24        // For negative exponent, calculate reciprocal of positive power
25        // Cast n to long long to handle INT_MIN overflow case
26        return n >= 0 ? fastPower(x, n) : 1.0 / fastPower(x, -(long long)n);
27    }
28};
29
1/**
2 * Calculates x raised to the power n (x^n) using binary exponentiation
3 * @param x - The base number
4 * @param n - The exponent (can be positive, negative, or zero)
5 * @returns The result of x^n
6 */
7function myPow(x: number, n: number): number {
8    /**
9     * Helper function that performs fast exponentiation using binary representation
10     * Uses the property: x^n = (x^2)^(n/2) when n is even, x * x^(n-1) when n is odd
11     * @param base - The base number to be raised to a power
12     * @param exponent - The positive exponent
13     * @returns The result of base^exponent
14     */
15    const quickPower = (base: number, exponent: number): number => {
16        let result = 1;
17      
18        // Process each bit of the exponent from right to left
19        while (exponent > 0) {
20            // If the current bit is 1, multiply result by current base
21            if (exponent & 1) {
22                result *= base;
23            }
24            // Square the base for the next bit position
25            base *= base;
26            // Right shift to process the next bit
27            exponent >>>= 1;
28        }
29      
30        return result;
31    };
32  
33    // Handle negative exponents by calculating 1 / (x^|n|)
34    return n >= 0 ? quickPower(x, n) : 1 / quickPower(x, -n);
35}
36

Time and Space Complexity

The time complexity is O(log n), where n is the absolute value of the exponent. This is achieved through the binary exponentiation algorithm (also known as exponentiation by squaring). The while loop iterates through each bit of n, and since n is right-shifted by 1 position in each iteration (n >>= 1), the loop runs approximately log₂(n) times.

The space complexity is O(1) as the algorithm only uses a constant amount of extra space. The variables ans and a are the only additional storage used, regardless of the input size. The helper function qpow is called at most once (not recursively), so there's no additional stack space consumption beyond the initial function call.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow with Minimum Integer Value

The Pitfall: When handling negative exponents, the code uses -n to convert a negative exponent to positive. However, in many programming languages, the minimum integer value (e.g., -2^31 in 32-bit systems) cannot be negated because its absolute value exceeds the maximum positive integer value (2^31 - 1). This causes integer overflow.

For example, if n = -2147483648 (minimum 32-bit integer), then -n would theoretically be 2147483648, which exceeds the maximum 32-bit integer 2147483647.

Solution: Convert n to a long integer type before negation to avoid overflow:

def myPow(self, x: float, n: int) -> float:
    def quick_power(base: float, exponent: int) -> float:
        result = 1.0
        while exponent > 0:
            if exponent & 1:
                result *= base
            base *= base
            exponent >>= 1
        return result
  
    # Convert to long to handle minimum integer value
    long_n = n
    if long_n >= 0:
        return quick_power(x, long_n)
    else:
        return 1.0 / quick_power(x, -long_n)

2. Special Edge Cases Not Handled

The Pitfall: The basic implementation doesn't explicitly handle certain edge cases that might cause issues:

  • x = 0 with negative exponent (division by zero)
  • x = 1 or x = -1 with very large exponents (can be optimized)
  • x = 0 with n = 0 (mathematically undefined but often treated as 1)

Solution: Add explicit checks for these special cases:

def myPow(self, x: float, n: int) -> float:
    # Handle special cases
    if x == 0:
        if n < 0:
            # 0^negative is undefined (division by zero)
            raise ValueError("Cannot raise 0 to a negative power")
        return 0 if n > 0 else 1  # 0^0 is typically defined as 1
  
    if x == 1:
        return 1
  
    if x == -1:
        return -1 if n % 2 == 1 else 1
  
    # Continue with regular algorithm...
    def quick_power(base: float, exponent: int) -> float:
        result = 1.0
        while exponent > 0:
            if exponent & 1:
                result *= base
            base *= base
            exponent >>= 1
        return result
  
    long_n = n
    if long_n >= 0:
        return quick_power(x, long_n)
    else:
        return 1.0 / quick_power(x, -long_n)

3. Floating-Point Precision Issues

The Pitfall: When dealing with very large exponents or very small/large base values, floating-point arithmetic can lead to precision loss or overflow/underflow. Repeated multiplication can accumulate rounding errors.

Solution: While complete precision cannot be guaranteed with floating-point arithmetic, you can add checks for potential overflow/underflow:

def myPow(self, x: float, n: int) -> float:
    import sys
  
    def quick_power(base: float, exponent: int) -> float:
        result = 1.0
      
        while exponent > 0:
            if exponent & 1:
                result *= base
                # Check for overflow/underflow
                if result == float('inf') or result == float('-inf'):
                    return result
                if abs(result) < sys.float_info.min and result != 0:
                    return 0.0  # Underflow to zero
          
            base *= base
            # Check base for overflow
            if base == float('inf'):
                return float('inf') if exponent & 1 else result
          
            exponent >>= 1
      
        return result
  
    # Rest of the implementation...

These solutions ensure the algorithm handles edge cases correctly and provides more robust behavior in production environments.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used to implement priority queue?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More