栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

使用递归的幂函数

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用递归的幂函数

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

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

因此,让我们从n个正数开始,然后从那里开始。

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

实际上,由于a since =a⨯(aⁿ⁻¹)在数学上是正确的,因此幼稚的方法将与您创建的方法非常相似:

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。在这里,有一个数学的事实,可以帮助我们:一个N 1 + N
2 = A 区N1 ⨯a 氮气。

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

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

因此,对于偶数,使用n / 2⨯an / 2;对于奇数,使用a⨯a n / 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的幂中获得了32、16、8、4、2、1,然后完成了。每一步只有一个或两个乘法,只有六个步。这是O(log
n)。

所有这些的结论是:

  1. 为了获得O(log n),我们需要递归,该递归在每个步骤上只占n的一部分,而不只是n-1或n-任何东西。
  2. 但是,这只是故事的一部分。我们需要注意不要多次调用递归,因为在一个步骤中使用多个递归调用会创建指数复杂度,而使用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;    }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/438532.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号