java的幂函数_使用递归的幂函数

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

对于正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⨯an 2。

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

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

因此,对于偶数使用n / 2⨯an / 2,对于奇数,使用a⨯an / 2⨯an / 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)。

所有这一切的结论是:

要得到一个O(log n),我们需要递归,它在每一步上只有n的一小部分,而不仅仅是n - 1或n - 任何东西。

但这一部分只是故事的一部分。我们需要注意不要多次调用递归,因为在一个步骤中使用几个递归调用会产生指数复杂性,使用一小部分n来取消。

醇>

最后,我们准备好照顾负数。我们只需得到倒数⅟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;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值