Compute x to the power of y
03 Dec 2019 | 5 minutes to readProblem
In this post, we wish to calculate \(x^y\). This seems like a pretty simple problem to solve! Let’s formulate the problem first:
We want function
power(x, y)
that accepts two integer inputsx
andy
returns \(x^y\).
By definition, \(x^y\) is described as the following:
if y is positive:
power(x,y) == x * x * ... * x
(where there are y number of x's)
ex. power(2, 3) == 2 * 2 * 2 == 8
if y is 0:
power(x,y) == 1
ex. power(1000, 0) == 1
if y is negative:
power(x,y) == 1/x * 1/x * ... * 1/x
(where there are y number of (1/x)'s)
ex. power(2, -3) == (1/2) * (1/2) * (1/2)
== (1/8)
== 1/(power(2, 3))
Solution
Let’s assume that there is no overflow and underflow.
Brute-force Approach
Let’s start with considering y >= 0
case only, the simplest approach is multiplying x
by itself y-1
times.
def power(x,y):
result = 1.0
for i in range(y):
result *= x
return result
The time complexity of such algorithm is O(y)
since we perform y-1
multiplications. Hence, the algorithm will get slower as we increase the value of y
.
Recursive Approach
The above solution will perform well for small value of y
. However, the run time will grow linearly with the size of y
. The key idea to improve this algorithm is to reduce the number of multiplication required.
Exponential Properties
Here are some exponential properties that we will use in this post.
- Power to power: $$(a^m)^n = a^{mn}$$
- Product of like bases: $$a^ma^n = a^{m+n}$$
Let’s consider the following problem: \(1.1^{20}\). Using out brute-force algorithm, we would need to perform 19 multiplcations. However, using the exponential property, we can breakdown the problem like this, $(1.1^{10})^2$, then we only need to calcualte \(9 + 1 = 10\) calculations only! By expanding this further, we can reduce the multiplication down to 5.
\[(1.1^{10})^2 = (((1.1^5))^2)^2 = ((1.1 * (1.1^2)^2)^2)^2\]So what is the pattern here? Recursion! The pattern here looks like the following: \(pow(a, n) = \begin{cases} 1, & \text{$n = 0$} \\ pow(a, n/2)^2, & \text{if $n$ is even}\\ pow(a, n/2)^2 * a, & \text{if $n$ is odd} \end{cases}\)
Not let’s code this:
def power(x,y):
if y == 0:
return 1
temp = power(x, int(y/2))
if y % 2 == 0:
return temp * temp
else:
return x * temp * temp
Notice that we are using temp
variable to not perform power(x, int(y/2))
twice. The run time of this is O(log(y))
as we are dividing y
into its half at every iteration.
To support negative y
, we can modify it like the following:
def power(x,y):
if y < 0:
x, y = 1.0 / x, -y
if y == 0:
return 1
temp = power(x, int(y/2))
if y % 2 == 0:
return temp * temp
else:
return x * temp * temp
Binary Expression Approach
This is the solution provided by Elements of Programming Interviews
book. I will write down the solution here first.
def power(x, y):
result, pow = 1.0, y
if y < 0:
pow, x = -pow, 1.0 / x
while pow:
if pow & 1:
result *= x;
x, pow = x * x, pow >> 1
return result
The idea behind is same as recursive approach; however it utilizes binary representation of y
and product of like bases exponential property. Let’s consider the following expresions:
\(x^{10} = x^{(1010)_2}
= x^{(101)_2 + (101)_2}
= x^{(101)_2} * x^{(101)_2}
= x^5 * x^5\)
\(x^{5} = x^{(101)_2}
= x^{(100)_2 + (1)_2}
= x^{(100)_2} * x^{(1)_2}
= x^{(10)_2} * x^{(10)_2} * x^{(1)_2}
= x^2 * x^2 * x\)
So to generalize, if the LSB of y is 0
(== even), the result is \((x^{y/2})^2\); otherwise, \(x * (x^{y/2})^2\)
Conclusion
By using mathematical property, we were able to reduce the time complexity through recusive approach!
References
- https://studentsuccess.asu.edu/sites/default/files/exponentialandlogrithmicproperties.pdf