Fast Power (Exponentiation by Squaring)
Write your own version of pow(x, n) -- the function that raises a base x to an exponent n, i.e. produces x^n.
Negative exponents must work too: x^(-n) means 1 / x^n.
210 = 1024.Fast Power
base x, exponent n ➔ halve exponent repeatedly ➔ O(log N) power| 1 | def my_pow(x: float, n: int) -> float: |
| 2 | """Binary Exponentiation: computes x^n by squaring. |
| 3 | Time: O(log N) | Space: O(1) |
| 4 | """ |
| 5 | if n < 0: |
| 6 | x = 1.0 / x # x^-n == (1/x)^n |
| 7 | n = -n |
| 8 | |
| 9 | result = 1.0 |
| 10 | curr_product = x # Holds x^(2^i) for the current bit i |
| 11 | |
| 12 | while n > 0: |
| 13 | if n % 2 == 1: |
| 14 | result *= curr_product # Bit is set: multiply this power in |
| 15 | curr_product *= curr_product # Square for the next bit |
| 16 | n //= 2 # Move to the next bit |
| 17 | |
| 18 | return result |
result * curr_product ** n equals the original x ** n.Write n in binary: x^13 = x^8 · x^4 · x^1, so only the powers x^1, x^2, x^4, ... are ever needed. curr_product squares once per bit, and result takes it in only when the current bit of n is 1 (n % 2 == 1). It is correct because result * curr_product ** n stays equal to the original x ** n every round, and the loop ends with n = 0. The trap is a negative n: flip first with x = 1 / x and n = -n (in Java or C++ copy n into 64 bits first, since -n overflows for -231).
See it run
Fast Power, step by step
x 2n -5result 1curr_product 2
Each column is one round. The rows are the bit of n, curr_product at the start of the round, and result at the end of the round.
bitthe bit of n tested in this roundcurrthe value of curr_product in this roundresultresult after it takes curr_product insquaredcurr_product after it was squared for the next round
my_pow(2, -5). The board lists the bits of 5 from the lowest bit up, one column per round.
Gotcha 1 of 4
- The trap
Gotcha 1: Negating
nwithout replacingxby1 / xMistakeif n < 0: n = -nFixif n < 0: x = 1.0 / x n = -nWhat happens: For a negative
n,x ** nequals(1 / x) ** -n. The loop only handlesn >= 0. Withn = -nalone, it computesx ** -n. Somy_pow(2.0, -2)returns 4.0 instead of 0.25.Example: x = 2.0, n = -2Mistake: returns 4.0Fix: returns 0.25
- You must know which of many numbers are prime, as in Count Primes (LC 204), not the value of one huge power. See Sieve of Eratosthenes
Deep Dive
Why only about log n squarings are needed
A direct loop multiplies x by itself n times. For n = 10**9, that is a billion steps. Fast power uses the binary form of n.
Write n in binary. For 13, that is 1101, which means 13 = 8 + 4 + 1. So x^13 = x^8 * x^4 * x^1. Only the powers x^1, x^2, x^4, x^8, ... are needed, and each one is the square of the previous one.
n has about log2(n) bits. For n = 10**9, that is 30 bits, so the loop runs 30 times and not a billion times. The space is O(1): two numbers, result and curr_product.
The invariant: result * curr_product ** n stays equal to x ** n
Before each round, result * curr_product ** n equals the original x ** n. At the start, result = 1, curr_product = x and n is the input, so it holds.
Case n is odd. The round sets result to result * curr_product, then curr_product to its square, then n to n // 2. Write n = 2m + 1. Then curr_product ** n = curr_product * (curr_product 2) m. The first factor moved into result and the rest is the new curr_product ** m. So the product is unchanged.
Case n is even. Write n = 2m. Then curr_product n = (curr_product 2) ** m. Squaring and halving keeps the product the same, and result does not change.
The loop ends when n = 0. Then curr_product ** 0 is 1, so result equals the original x ** n.
Trace for my_pow(2.0, -5): the negative exponent step
n = -5 is negative, so the code sets x = 1.0 / x, which is 0.5, and n = 5. Now the loop computes 0.5 ** 5, and x ** n with the original values is 2 ** -5. These are equal.
The binary form of 5 is 101. Round 0: the bit is 1, so result = 1 * 0.5 = 0.5. Then curr_product becomes 0.25 and n becomes 2. Round 1: the bit is 0, so result stays 0.5. curr_product becomes 0.0625 and n becomes 1. Round 2: the bit is 1, so result = 0.5 * 0.0625 = 0.03125. Then n becomes 0 and the loop ends.
The answer is 0.03125, which is 2 ** -5. Without the x = 1.0 / x step, the code would compute 2 ** 5 = 32.
The Pow(x, n) (LC 50) examples work the same way. For x = 2.0 and n = 10, the result is 1024.0. For x = 2.0 and n = -2, it is 0.25.
Reuse: powers modulo a number
Many counting problems ask for the answer "modulo MOD" with MOD = 10**9 + 7. The same loop works if every multiplication is followed by % MOD: result = result * curr_product % MOD and curr_product = curr_product * curr_product % MOD. The values stay below MOD, so they stay small. Python's pow(base, exp, MOD) runs this loop.
Count Good Numbers (LC 1922) has a length up to 10^15. The answer is 5^a * 4^b for two counts a and b that sum to the length. Each power is one call of this loop. Maximize Number of Nice Divisors (LC 1808) and Count Collisions of Monkeys on a Polygon (LC 2550) also end in one power with a huge exponent.
A subtraction after the power needs one more step. pow(2, n, MOD) - 2 can be negative, because the reduced value can be smaller than 2. Add MOD back and take % MOD again.
Super Pow (LC 372) has the exponent as a list of digits. It applies the same square-and-multiply idea digit by digit.
60-Second Pitch
"A loop of n multiplications is too slow for a large n. I write n in binary and use the powers x^1, x^2, x^4, ..., each the square of the one before. curr_product squares every round. result multiplies by curr_product only when n % 2 == 1. Then n halves. The invariant is that result * curr_product ** n equals the original x ** n, so at n = 0 the result is correct. The time is O(log n) and the space is O(1). A negative n needs x = 1.0 / x and n = -n first, and in Java or C++ I widen n to 64 bits before negating. With a modulus, I reduce after every multiplication."
Common Questions
- Why is the time
O(log n)? Each round halves
n, so the loop runs once per bit ofn. Forn = 10**9, that is 30 rounds. Each round does at most two multiplications.- Why does the result stay correct when
nis odd? Write
n = 2m + 1. Thencurr_product ** n = curr_product * (curr_product 2) m. The first factor goes intoresult, and the second part is the newcurr_product ** m. Soresult * curr_product ** ndoes not change.- How do you handle a negative exponent?
Use
x n = (1 / x) -n. Setx = 1.0 / xandn = -n, then run the loop. In Java or C++, copyninto a 64-bit variable first, because-noverflows for-231.- How do you use it when the answer is modulo 10^9 + 7?
Apply
% MODafter each multiplication to bothresultandcurr_product. Both stay belowMOD. In Python,pow(base, exp, MOD)does the same. Count Good Numbers (LC 1922) needs this with an exponent up to 10^15.- Can you write it with recursion?
Yes. Compute
half = power(x, n // 2), then returnhalf * half, timesxwhennis odd. The depth is aboutlog2(n). The loop in the card usesO(1)space, so it is the better choice.
SWE Reasoning
Spot it
- The exponent is huge (10^9 or more), so a loop of
nmultiplications is too slow (Pow(x, n) (LC 50)). - The answer is a repeated product "modulo 10^9 + 7" (Count Good Numbers (LC 1922), Count Collisions of Monkeys on a Polygon (LC 2550)).
- A count has the form
a^bfor a largeb(Maximize Number of Nice Divisors (LC 1808), Super Pow (LC 372)). - The task is a power, not a search for which numbers are prime. For prime tests, use Sieve of Eratosthenes.
The invariant, step by step
result = 1,curr_product = xandnis the exponent.result * curr_product ** nequalsx ** n.- If
nis odd, move one factor ofcurr_productintoresult. - Square
curr_productand halven(integer division). The productresult * curr_product ** ndoes not change. - Each round removes one bit of
n, so there are aboutlog2(n)rounds. - When
nis 0,curr_product ** 0is 1, andresultequalsx ** n.
Traps
- Negating
nwithoutx = 1.0 / x.my_pow(2.0, -2)returns 4.0 instead of 0.25. -nin a 32-bitintin Java or C++. Forn = -231, the value231does not fit in 32 bits. Copyninto a 64-bit variable first.- Reducing modulo
MODonly at the end. The numbers grow to many digits. In Python the answer stays correct but each multiplication gets slower. In Java or C++, a 64-bit value overflows. - Subtracting after
pow(2, n, MOD).pow(2, 500000003, 10**9 + 7) - 2is -1. Use(pow(2, n, MOD) - 2 + MOD) % MOD, which gives 1000000006. - Multiplying
resultbycurr_productevery round. Only the rounds with an oddntake the factor.
Practice
Fast Power (Exponentiation by Squaring)
Write your own version of pow(x, n) -- the function that raises a base x to an exponent n, i.e. produces x^n.
Negative exponents must work too: x^(-n) means 1 / x^n.
210 = 1024.Fast Power
base x, exponent n ➔ halve exponent repeatedly ➔ O(log N) power| 1 | def my_pow(x: float, n: int) -> float: |
| 2 | """Binary Exponentiation: computes x^n by squaring. |
| 3 | Time: O(log N) | Space: O(1) |
| 4 | """ |
| 5 | if n < 0: |
| 6 | x = 1.0 / x # x^-n == (1/x)^n |
| 7 | n = -n |
| 8 | |
| 9 | result = 1.0 |
| 10 | curr_product = x # Holds x^(2^i) for the current bit i |
| 11 | |
| 12 | while n > 0: |
| 13 | if n % 2 == 1: |
| 14 | result *= curr_product # Bit is set: multiply this power in |
| 15 | curr_product *= curr_product # Square for the next bit |
| 16 | n //= 2 # Move to the next bit |
| 17 | |
| 18 | return result |
result * curr_product ** n equals the original x ** n.Write n in binary: x^13 = x^8 · x^4 · x^1, so only the powers x^1, x^2, x^4, ... are ever needed. curr_product squares once per bit, and result takes it in only when the current bit of n is 1 (n % 2 == 1). It is correct because result * curr_product ** n stays equal to the original x ** n every round, and the loop ends with n = 0. The trap is a negative n: flip first with x = 1 / x and n = -n (in Java or C++ copy n into 64 bits first, since -n overflows for -231).
See it run
Fast Power, step by step
x 2n -5result 1curr_product 2
Each column is one round. The rows are the bit of n, curr_product at the start of the round, and result at the end of the round.
bitthe bit of n tested in this roundcurrthe value of curr_product in this roundresultresult after it takes curr_product insquaredcurr_product after it was squared for the next round
my_pow(2, -5). The board lists the bits of 5 from the lowest bit up, one column per round.
Gotcha 1 of 4
- The trap
Gotcha 1: Negating
nwithout replacingxby1 / xMistakeif n < 0: n = -nFixif n < 0: x = 1.0 / x n = -nWhat happens: For a negative
n,x ** nequals(1 / x) ** -n. The loop only handlesn >= 0. Withn = -nalone, it computesx ** -n. Somy_pow(2.0, -2)returns 4.0 instead of 0.25.Example: x = 2.0, n = -2Mistake: returns 4.0Fix: returns 0.25
- You must know which of many numbers are prime, as in Count Primes (LC 204), not the value of one huge power. See Sieve of Eratosthenes
Deep Dive
Why only about log n squarings are needed
A direct loop multiplies x by itself n times. For n = 10**9, that is a billion steps. Fast power uses the binary form of n.
Write n in binary. For 13, that is 1101, which means 13 = 8 + 4 + 1. So x^13 = x^8 * x^4 * x^1. Only the powers x^1, x^2, x^4, x^8, ... are needed, and each one is the square of the previous one.
n has about log2(n) bits. For n = 10**9, that is 30 bits, so the loop runs 30 times and not a billion times. The space is O(1): two numbers, result and curr_product.
The invariant: result * curr_product ** n stays equal to x ** n
Before each round, result * curr_product ** n equals the original x ** n. At the start, result = 1, curr_product = x and n is the input, so it holds.
Case n is odd. The round sets result to result * curr_product, then curr_product to its square, then n to n // 2. Write n = 2m + 1. Then curr_product ** n = curr_product * (curr_product 2) m. The first factor moved into result and the rest is the new curr_product ** m. So the product is unchanged.
Case n is even. Write n = 2m. Then curr_product n = (curr_product 2) ** m. Squaring and halving keeps the product the same, and result does not change.
The loop ends when n = 0. Then curr_product ** 0 is 1, so result equals the original x ** n.
Trace for my_pow(2.0, -5): the negative exponent step
n = -5 is negative, so the code sets x = 1.0 / x, which is 0.5, and n = 5. Now the loop computes 0.5 ** 5, and x ** n with the original values is 2 ** -5. These are equal.
The binary form of 5 is 101. Round 0: the bit is 1, so result = 1 * 0.5 = 0.5. Then curr_product becomes 0.25 and n becomes 2. Round 1: the bit is 0, so result stays 0.5. curr_product becomes 0.0625 and n becomes 1. Round 2: the bit is 1, so result = 0.5 * 0.0625 = 0.03125. Then n becomes 0 and the loop ends.
The answer is 0.03125, which is 2 ** -5. Without the x = 1.0 / x step, the code would compute 2 ** 5 = 32.
The Pow(x, n) (LC 50) examples work the same way. For x = 2.0 and n = 10, the result is 1024.0. For x = 2.0 and n = -2, it is 0.25.
Reuse: powers modulo a number
Many counting problems ask for the answer "modulo MOD" with MOD = 10**9 + 7. The same loop works if every multiplication is followed by % MOD: result = result * curr_product % MOD and curr_product = curr_product * curr_product % MOD. The values stay below MOD, so they stay small. Python's pow(base, exp, MOD) runs this loop.
Count Good Numbers (LC 1922) has a length up to 10^15. The answer is 5^a * 4^b for two counts a and b that sum to the length. Each power is one call of this loop. Maximize Number of Nice Divisors (LC 1808) and Count Collisions of Monkeys on a Polygon (LC 2550) also end in one power with a huge exponent.
A subtraction after the power needs one more step. pow(2, n, MOD) - 2 can be negative, because the reduced value can be smaller than 2. Add MOD back and take % MOD again.
Super Pow (LC 372) has the exponent as a list of digits. It applies the same square-and-multiply idea digit by digit.
60-Second Pitch
"A loop of n multiplications is too slow for a large n. I write n in binary and use the powers x^1, x^2, x^4, ..., each the square of the one before. curr_product squares every round. result multiplies by curr_product only when n % 2 == 1. Then n halves. The invariant is that result * curr_product ** n equals the original x ** n, so at n = 0 the result is correct. The time is O(log n) and the space is O(1). A negative n needs x = 1.0 / x and n = -n first, and in Java or C++ I widen n to 64 bits before negating. With a modulus, I reduce after every multiplication."
Common Questions
- Why is the time
O(log n)? Each round halves
n, so the loop runs once per bit ofn. Forn = 10**9, that is 30 rounds. Each round does at most two multiplications.- Why does the result stay correct when
nis odd? Write
n = 2m + 1. Thencurr_product ** n = curr_product * (curr_product 2) m. The first factor goes intoresult, and the second part is the newcurr_product ** m. Soresult * curr_product ** ndoes not change.- How do you handle a negative exponent?
Use
x n = (1 / x) -n. Setx = 1.0 / xandn = -n, then run the loop. In Java or C++, copyninto a 64-bit variable first, because-noverflows for-231.- How do you use it when the answer is modulo 10^9 + 7?
Apply
% MODafter each multiplication to bothresultandcurr_product. Both stay belowMOD. In Python,pow(base, exp, MOD)does the same. Count Good Numbers (LC 1922) needs this with an exponent up to 10^15.- Can you write it with recursion?
Yes. Compute
half = power(x, n // 2), then returnhalf * half, timesxwhennis odd. The depth is aboutlog2(n). The loop in the card usesO(1)space, so it is the better choice.
SWE Reasoning
Spot it
- The exponent is huge (10^9 or more), so a loop of
nmultiplications is too slow (Pow(x, n) (LC 50)). - The answer is a repeated product "modulo 10^9 + 7" (Count Good Numbers (LC 1922), Count Collisions of Monkeys on a Polygon (LC 2550)).
- A count has the form
a^bfor a largeb(Maximize Number of Nice Divisors (LC 1808), Super Pow (LC 372)). - The task is a power, not a search for which numbers are prime. For prime tests, use Sieve of Eratosthenes.
The invariant, step by step
result = 1,curr_product = xandnis the exponent.result * curr_product ** nequalsx ** n.- If
nis odd, move one factor ofcurr_productintoresult. - Square
curr_productand halven(integer division). The productresult * curr_product ** ndoes not change. - Each round removes one bit of
n, so there are aboutlog2(n)rounds. - When
nis 0,curr_product ** 0is 1, andresultequalsx ** n.
Traps
- Negating
nwithoutx = 1.0 / x.my_pow(2.0, -2)returns 4.0 instead of 0.25. -nin a 32-bitintin Java or C++. Forn = -231, the value231does not fit in 32 bits. Copyninto a 64-bit variable first.- Reducing modulo
MODonly at the end. The numbers grow to many digits. In Python the answer stays correct but each multiplication gets slower. In Java or C++, a 64-bit value overflows. - Subtracting after
pow(2, n, MOD).pow(2, 500000003, 10**9 + 7) - 2is -1. Use(pow(2, n, MOD) - 2 + MOD) % MOD, which gives 1000000006. - Multiplying
resultbycurr_productevery round. Only the rounds with an oddntake the factor.