使用递归的幂函数

时间:2014-11-01 13:48:00

标签: java recursion pow

我必须用Java编写一个power方法。它接收两个整数,如果它们是正数或负数则无关紧要。它应该具有O(logN)的复杂性。它还必须使用递归。我当前的代码得到两个数字,但我保持输出的结果为零,我无法弄清楚原因。

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;
    }
}

9 个答案:

答案 0 :(得分:26)

让我们从一些数学事实开始:

  • 对于正n,aⁿ=a⨯a⨯...⨯a次
  • 对于负n,aⁿ=⅟a - ⁿ=⅟(a⨯a⨯...⨯a)。这意味着 a 不能为零。
  • 对于n = 0,aⁿ= 1,即使 a 为零或负数。

所以让我们从积极的n案例开始,然后从那里开始工作。

由于我们希望我们的解决方案是递归的,我们必须找到一种方法来定义基于较小n的a,并从那里开始工作。人们通常认为递归的方法是尝试找到n-1的解决方案,并从那里开始工作。

事实上,由于aⁿ=a⨯(aⁿ-1)在数学上是正确的,所以天真的方法与你创造的方法非常相似:

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    return ( a * pow(a,n-1));
}

然而,这种复杂性是O(n)。为什么?因为对于n = 0,它不进行任何乘法运算。对于n = 1,它进行一次乘法。对于n = 2,它调用pow(a,1),我们知道它是一个乘法,并将它乘以一次,所以我们有两次乘法。每个递归步骤都有一个乘法,有n个步骤。所以这是O(n)。

为了使这个O(log n),我们需要将每个步骤应用于n的分数而不仅仅是n-1。在这里,有一个数学事实可以帮助我们:a n 1 + n 2 = a n 1 ⨯a n 2

这意味着我们可以将aⁿ计算为 n / 2 ⨯a n / 2

但如果n是奇数会怎么样?像a⁹这样的东西将是 4.5 ⨯a 4.5 。但我们在这里讨论的是整数幂。处理分数是完全不同的事情。幸运的是,我们可以将其表述为a⨯a⁴⨯a⁴。

因此,对于偶数使用 n / 2 ⨯a n / 2 ,对于奇数,使用a⨯a n / 2 ⨯a n / 2 (整数除法,给我们9/2 = 4)。

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    if ( n % 2 == 1 ) {
        // Odd n
        return a * pow( a, n/2 ) * pow(a, n/2 );
    } else {
        // Even n
        return pow( a, n/2 ) * pow( a, n/2 );
    }
}

这实际上给了我们正确的结果(对于正n,即)。但事实上,这里的复杂性再次是O(n)而不是O(log n)。为什么?因为我们两次计算权力。这意味着我们实际上在下一级别调用它4次,在下一级别调用8次,依此类推。递归步骤的数量是指数级的,所以这取消了我们通过将n除以2所做的假设保存。

但事实上,只需要进行一次小修正:

public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    int powerOfHalfN = pow( a, n/2 );
    if ( n % 2 == 1 ) {
        // Odd n
        return a * powerOfHalfN * powerOfHalfN;
    } else {
        // Even n
        return powerOfHalfN * powerOfHalfN;
    }
}

在这个版本中,我们只调用一次递归。因此,我们从64,16,8,4,2,1和非常快的速度得到64的功率。每一步只有一到两次乘法,而且只有六步。这是O(log n)。

所有这一切的结论是:

  1. 要得到一个O(log n),我们需要递归,它在每一步上只有n的一小部分,而不仅仅是n - 1或n - 任何东西。
  2. 但这一部分只是故事的一部分。我们需要注意不要多次调用递归,因为在一个步骤中使用几个递归调用会产生指数复杂性,使用一小部分n来取消。
  3. 最后,我们准备好照顾负数。我们只需得到倒数⅟a - ⁿ。有两件重要的事情需要注意:

    • 不允许除以零。也就是说,如果你得到a = 0,你不应该执行计算。在Java中,我们在这种情况下抛出异常。最合适的现成例外是IllegalArgumentException。这是一个RuntimeException,因此您不需要在方法中添加throws子句。如果您在阅读参数时在main方法中捕获它或阻止这种情况发生将会很好。
    • 你不能再返回一个整数(事实上,我们应该使用long,因为我们遇到了int相当低的幂的整数溢出 - 因为结果可能是小数的。

    因此我们定义方法以使其返回double。这意味着我们还必须修复powerOfHalfN的类型。这是结果:

    public static double pow(int a, int n) {
        if (n == 0) {
            return 1.0;
        }
        if (n < 0) {
            // Negative power.
            if (a == 0) {
                throw new IllegalArgumentException(
                        "It's impossible to raise 0 to the power of a negative number");
            }
            return 1 / pow(a, -n);
        } else {
            // Positive power
    
            double powerOfHalfN = pow(a, n / 2);
            if (n % 2 == 1) {
                // Odd n
                return a * powerOfHalfN * powerOfHalfN;
            } else {
                // Even n
                return powerOfHalfN * powerOfHalfN;
            }
        }
    }
    

    请注意,处理负数n的部分仅用于递归的顶级。一旦我们递归地调用pow(),它总是带有正数,并且符号在达到0之前不会改变。

    这应该是适合您锻炼的解决方案。但是,我个人不喜欢最后的if,所以这是另一个版本。你能说出为什么会这样做吗?

    public static double pow(int a, int n) {
        if (n == 0) {
            return 1.0;
        }
        if (n < 0) {
            // Negative power.
            if (a == 0) {
                throw new IllegalArgumentException(
                        "It's impossible to raise 0 to the power of a negative number");
            }
            return 1 / pow(a, -n);
        } else {
            // Positive power
            double powerOfHalfN = pow(a, n / 2);
            double[] factor = { 1, a };
            return factor[n % 2] * powerOfHalfN * powerOfHalfN;
        }
    }
    

答案 1 :(得分:1)

注意:

float result = 0;

result = result * pow( a, n+1);

这就是你得到零结果的原因。 相反,它建议像这样工作:

result = a * pow( a, n+1);

答案 2 :(得分:1)

除了将result初始化为0的错误之外,还有一些其他问题:

  1. 您对否定n的计算错误。请记住a^n == 1/(a^(-n))
  2. 如果n不是整数,则计算要复杂得多,并且您不支持它。如果你不需要支持,我不会感到惊讶。
  3. 为了达到O(log n)表现,你应该采用分而治之的策略。即a^n == a^(n/2)*a^(n/2)

答案 3 :(得分:1)

# a pow n = a pow n%2 * square(a) pow(n//2)
# a pow -n = (1/a) pow n
from math import inf
def powofn(a, n):
    if n == 0:
        return 1
    elif n == 1:
        return a
    elif n < 0:
        if a == 0 : return inf
        return powofn(1/a, -n)
    else:
        return powofn(a, n%2) * powofn(a*a, n//2)

答案 4 :(得分:0)

一个好的规则是远离键盘,直到algorythm准备好。你所做的显然是O(n)。

正如Eran建议的那样,为了得到O(log(n))复杂度,你必须在每次迭代时将n除以2。

结束条件:

  • n == 0 =&gt; 1
  • n == 1 =&gt;一个

特例:

  • n&lt; 0 =&gt; 1. / pow(a,-n)//注意1.得到一个双...

正常情况:

  • m = n / 2
  • result = pow(a,n)
  • result = resul * resul //避免计算两次
  • 如果n是奇数(n%2!= 0)=&gt;结果* = a

这个algorythm在O(log(n))中 - 由你来编写正确的java代码

但是有人告诉你:n 必须是整数(肯定为负,但整数

答案 5 :(得分:0)

这是一种不那么混乱的方式,至少如果你不担心额外的乘法。 :

public static double pow(int base,int exponent) {
        if (exponent == 0) {
            return 1;
        }
        if (exponent < 0) {
            return 1 / pow(base, -exponent);
        }
        else {
            double results = base * pow(base, exponent - 1);
            return results;
        }
    }

答案 6 :(得分:0)

import java.io.*;
import java.util.*;
public class CandidateCode {
    public static void main(String args[] ) throws Exception {

    Scanner sc = new Scanner(System.in);
    int m = sc.nextInt();
    int n = sc. nextInt();
     int result = power(m,n);
     System.out.println(result);
    }
     public static int power(int m, int n){
         if(n!=0)
            return (m*power(m,n-1));
        else 
            return 1;
     }

}

答案 7 :(得分:0)

尝试:

    public int powerN(int base, int n) {return n == 0 ? 1 : (n == 1 ? base : base*(powerN(base,n-1)));

答案 8 :(得分:-1)

好吧,我读了其他人发给她的解决方案,但让我明白了,这些答案给了你 正确且经过优化的解决方案,但您的解决方案也可以通过将float result = 0替换为float result = 1来工作。