Let's start with some mathematical facts:
- For positive n, aⁿ = a⨯a⨯ ... ⨯an times
- For negative n, aⁿ = ⅟a⁻ⁿ = ⅟ (a⨯a⨯ ... ⨯a). This means that a cannot be zero.
- For n = 0, aⁿ = 1, even if a is zero or negative.
So, let's start with the positive n case and work from there.
Since we want our solution to be recursive, we must find a way to determine aⁿ based on smaller n and work from there. Usually people think of recursion - try to find a solution for n-1 and work from there.
And indeed, since it is mathematically true that aⁿ = a⨯ (aⁿ⁻¹), the naive approach will be very similar to what you created:
public static int pow( int a, int n) { if ( n == 0 ) { return 1; } return ( a * pow(a,n-1)); }
However, the complexity of this is O (n). What for? Since for n = 0 it does not perform any multiplications. For n = 1, it is one multiplication. For n = 2, he calls pow (a, 1), which, as we know, is one multiplication and multiplies it once, so we have two multiplications. At each stage of the recursion, there is one multiplication, and there are n steps. So It O (n).
To do this O (log n), we need every step that needs to be applied to the fraction of n, and not just to n-1. Here again, there is a mathematical fact that can help us: a n₁ + n₂ = a n₁ ⨯a n₂ .
This means that we can calculate aⁿ as n / 2 ⨯a n / 2 .
But what happens if n is odd? something like a⁹ will be 4.5 ⨯a 4.5 . But here we are talking about whole forces. Processing fractions is a completely different matter. Fortunately, we can simply state it as a⨯a⁴⨯a⁴.
So, for an even number, use a n / 2 ⨯a n / 2 and for an odd number use a⨯ a n / 2 ⨯a n / 2 (integer division giving 9/2 = 4).
public static int pow( int a, int n) { if ( n == 0 ) { return 1; } if ( n % 2 == 1 ) {
This actually gives us the correct results (for positive n, that is). But actually the difficulty here is, again, O (n), not O (log n). What for? Because we calculate powers twice. This means that we actually call it 4 times at the next level, 8 times at the next level, etc. The number of recursion steps is exponential, so this is canceled with the preservation we made by dividing n by two.
But actually only a small correction is required:
public static int pow( int a, int n) { if ( n == 0 ) { return 1; } int powerOfHalfN = pow( a, n/2 ); if ( n % 2 == 1 ) {
In this version, we call recursion only once. Thus, we get, say, a power of 64, very quickly through 32, 16, 8, 4, 2, 1 and do it. Only one or two multiplications at each step, and there are only six steps. This is O (log n).
The conclusion from all of this:
- To get O (log n), we need a recursion that works for the fraction of n at each step, and not just for n - 1 or n - whatever.
- But a fraction is only part of the story. We need to be careful not to call recursion more than once, since using multiple recursive calls in one step creates exponential complexity that overrides the use of part n.
Finally, we are ready to take care of negative numbers. We just need to get a response ⅟a . There are two important things to notice:
- Do not divide by zero. That is, if you got a = 0, you should not perform the calculation. In Java, we throw an exception in this case. The most suitable prepared exception is an IllegalArgumentException. This is a RuntimeException exception, so you do not need to add a
throws to your method. It would be nice if you either caught it or didn’t allow such a situation in your main method when you read in the arguments. - You can no longer return an integer (in fact, we should use
long , because we run an integer overflow for fairly low powers with int ) - because the result can be fractional.
So, we define the method so that it returns twice. This means that we must also fix the type powerOfHalfN . And here is the result:
public static double pow(int a, int n) { if (n == 0) { return 1.0; } if (n < 0) {
Note that the part handling negative n is only used at the top level of recursion. As soon as we recursively call pow() , it is always with positive numbers, and the sign does not change until it reaches 0.
This should be an adequate solution for your exercise. However, I personally do not like if there at the end, so here is another version. Can you explain why this does the same?
public static double pow(int a, int n) { if (n == 0) { return 1.0; } if (n < 0) {