I have to write a power method in Java. It receives two ints and it doesn't matter if they are positive or negative numbers. It should have complexity of O(logN)
. It also must use recursion. My current code gets two numbers but the result I keep outputting is zero, and I can't figure out why.
import java.util.Scanner;
public class Powers {
public static void main(String[] args) {
float a;
float n;
float res;
Scanner in = new Scanner(System.in);
System.out.print("Enter int a ");
a = in.nextFloat();
System.out.print("Enter int n ");
n = in.nextFloat();
res = powers.pow(a, n);
System.out.print(res);
}
public static float pow(float a, float n) {
float result = 0;
if (n == 0) {
return 1;
} else if (n < 0) {
result = result * pow(a, n + 1);
} else if (n > 0) {
result = result * pow(a, n - 1);
}
return result;
}
}
A good rule is to get away from the keyboard until the algorythm is ready. What you did is obviously O(n).
As Eran suggested, to get a O(log(n)) complexity, you have to divide n by 2 at each iteration.
End conditions :
Special case :
Normal case :
This algorythm is in O(log(n)) - It's up to you to write correct java code from it
But as you were told : n must be integer (negative of positive ok, but integer)
pay attention to :
and
That's why you got a zero result. And instead it's suggested to work like this:
Beside the error of initializing
result
to 0, there are some other issues :n
is wrong. Remember thata^n == 1/(a^(-n))
.O(log n)
performance, you should use a divide and conquer strategy. i.e.a^n == a^(n/2)*a^(n/2)
.Let's start with some math facts:
So let's start from the positive n case, and work from there.
Since we want our solution to be recursive, we have to find a way to define aⁿ based on a smaller n, and work from there. The usual way people think of recursion is to try to find a solution for n-1, and work from there.
And indeed, since it's mathematically true that aⁿ = a⨯(aⁿ⁻¹), the naive approach would be very similar to what you created:
However, the complexity of this is O(n). Why? Because For n=0 it doesn't do any multiplications. For n=1, it does one multiplication. For n=2, it calls pow(a,1) which we know is one multiplication, and multiplies it once, so we have two multiplications. There is one multiplication in every recursion step, and there are n steps. So It's O(n).
In order to make this O(log n), we need every step to be applied to a fraction of n rather than just n-1. Here again, there is a math fact that can help us: an₁+n₂ = an₁⨯an₂.
This means that we can calculate aⁿ as an/2⨯an/2.
But what happens if n is odd? something like a⁹ will be a4.5⨯a4.5. But we are talking about integer powers here. Handling fractions is a whole different thing. Luckily, we can just formulate that as a⨯a⁴⨯a⁴.
So, for an even number use an/2⨯an/2, and for an odd number, use a⨯ an/2⨯an/2 (integer division, giving us 9/2 = 4).
This actually gives us the right results (for a positive n, that is). But in fact, the complexity here is, again, O(n) rather than O(log n). Why? Because we're calculating the powers twice. Meaning that we actually call it 4 times at the next level, 8 times at the next level, and so on. The number of recursion steps is exponential, so this cancels out with the supposed saving that we did by dividing n by two.
But in fact, only a small correction is needed:
In this version, we are calling the recursion only once. So we get from, say, a power of 64, very quickly through 32, 16, 8, 4, 2, 1 and done. Only one or two multiplications at each step, and there are only six steps. This is O(log n).
The conclusion from all this is:
Finally, we are ready to take care of the negative numbers. We simply have to get the reciprocal ⅟a⁻ⁿ. There are two important things to notice:
throws
clause to your method. It would be good if you either caught it or prevented such a situation from happening, in yourmain
method when you read in the arguments.long
, because we run into integer overflow for pretty low powers withint
) - because the result may be fractional.So we define the method so that it returns double. Which means we also have to fix the type of
powerOfHalfN
. And here is the result:Note that the part that handles a negative n is only used in the top level of the recursion. Once we call
pow()
recursively, it's always with positive numbers and the sign doesn't change until it reaches 0.That should be an adequate solution to your exercise. However, personally I don't like the
if
there at the end, so here is another version. Can you tell why this is doing the same?Here is a much less confusing way of doing it, at least if your not worred about the extra multiplications. :