改进的功率递归功能

时间:2018-12-06 00:04:52

标签: c

我正在尝试编写一个递归函数,以将一个数字升为另一个数字的幂,如果n为偶数或x ^ n = x *(x ^,则x ^ n =(x ^(n / 2)^ 2 (n-1)),如果n为奇数。

我已经考虑了很长时间,并尝试了一些方法 但老实说,我不知道如何解决这个问题,尤其是当n为奇数时。我编写了一些代码,以防n为偶数,但是我什至不知道发布它是否有意义,因为它背后的逻辑本身已经很糟糕了。

任何帮助表示赞赏!

int power(int x, int n){
  int result = 1;

  if(n % 2 == 0){
    n /= 2;
    for(; n > 0; n--){
      result *= x;
    }
    power(result, 2);
  }

  return result;

}

第二次尝试,没有for循环:

int power(int x, int n){
  int result = 1;

  if(n % 2 == 0){
    result *= x;
    power(x, n / 2);
  }

  return result;

}

3 个答案:

答案 0 :(得分:2)

这里是使用递归“ x ^ n =(x ^(n / 2)^ 2如果n为偶数,或者x ^ n = x *(x ^(n-1))如果使用n则”的直译:

int power(int x, int n){
    int temp;

    if(n % 2 == 0){
        // n is even
        temp = power(x, n / 2);
        return temp * temp;
    } else {
        // n is odd
        return x * power(x, n - 1);
    }
}

请注意,此代码是有意的错误(可能会“永远”递归并导致堆栈溢出),因为原始说明未包含针对n = 0情况的特殊处理。

对于更正确/更少文字的版本:

int power(int x, int n){
    int temp;

    if(n % 2 == 0){
        // n is even
        if(n == 0) {
            return 1;
        }
        temp = power(x, n / 2);
        return temp * temp;
    } else {
        // n is odd
        return x * power(x, n - 1);
    }
}

当然(因为可读性,可维护性和性能)这是比较糟糕的,因为要求很糟糕。忽略要求(并且不是递归的)的版本将是:

int power(int x, int n){
    int result = 1;

    while(n > 0) {
          if( (n & 1) != 0) {
              result *= x;
          }
          n >>= 1;
          x *= x;
    }
    return result;
}

主题外的

对于计算能力,如果将指数转换为二进制,则可以使用每个二进制数字作为标志来确定是否将结果与“每次迭代的平方”临时值相乘。例如,x**5(x**4) * (x**1)相同,因为5 == 101b

这在需要处理小数指数时非常重要/有用。例如,x**5.5(x**4) * (x**1) * (x**0.5)相同,因为5.5 == 101.1b

换句话说,从指数的小数点开始,一直到指数的整数部分的最高有效位,您可以if(next bit of exponent was set) { result *= temp}; temp *= temp;;然后从指数的小数点开始,一直到指数的小数部分的最低有效位,就可以if(next bit of exponent was set) { result *= temp}; temp = sqrt(temp);

当我第一次编写此答案时,最后一个示例代码是使用我在此描述的“将指数转换为二进制数字”方法编写的,而与“ x ^ n =(x ^(n / 2)^”无关2如果n是偶数,或者x ^ n = x *(x ^(n-1))如果n是奇数”,直到后来我才意识到原来问题中的方法最终是不同角度的等效代码。

答案 1 :(得分:1)

在进入代码之前,让我们看看您应该如何评估它,看看我们是否可以更好地理解它。我将使用monospace进行数学运算,而不是使用C代码来更轻松地区分它:

首先,出于稍后讨论的原因,我需要将您的规则更改为偶数。我将不写(x^(n/2))^2,而是写squared。您需要以与其他部分不同的方式处理此部分。除此之外,您问题中^的每个实例都意味着“我们需要使用相同的算法来计算此功效”。

这意味着您的算法是:

  • 如果n是偶数,则x^n = (x^(n/2)) squared
  • 如果n为奇数,则x^n = x*(x^(n-1))

让我们用一个简单的3^5示例来看看它是如何进行的。如果将其扩展,则由于5是奇数,因此最终会得到3 * (3^4)。您仍然不知道3^4是什么,因此您需要使用相同的算法再次将其扩展。由于4是偶数,因此将其计算为(3^2) squared,因此3^5的完整值为3 * ((3^2) squared)。同样,我们使用相同的算法评估^;因为2也是偶数,所以它变成3 * ((3^1 squared) squared)。这也有一个^;所以我们做同样的事情:使用上面的算法。由于1是奇数,因此变成3 * (((3 * 3^0) squared) squared)

现在,0是偶数,但是一旦您进入3^0,继续尝试使用较小的功率就没有意义了。由于0/20,因此您将回到起点,尝试再次评估3^0。因此,0需要作为特殊情况进行处理。这为算法添加了第三条规则:

  • 如果n0,则x^n = 1

在代码中,您需要先检查此项,然后再检查“如果n是偶数”规则,因为0是偶数,我们要先运行此检查。每个递归算法都有至少一个这样的特殊情况,有时甚至更多。术语是基本情况。¹

现在我们有了处理x^0的规则,我们可以继续查找3^5。我们的最后一步是3 * (((3 * 3^0) squared) squared)。使用我们的x^0规则,它变成3 * (((3 * 1) squared) squared)。由于此表达式中不再包含^,因此我们已完成递归。现在我们只需要计算结果:3 * ((3 squared) squared)。您仍然需要不使用此squared操作来解决^,但是由于指数是固定的,您可以将其写为乘法。如果这样做,您将得到3 * (9 squared),然后是3 * 81,然后是243,这是我们的最终答案。

在评论中,您问“函数如何知道幂表示乘以n乘以基数”。答案是“我们计算了3*1,然后对结果求平方两次,然后再乘以3。这就是所有乘法的结果:有些是3的完全乘法,并且是平方有效的乘法运算次数增加了一倍。”

¹递归的标准示例是阶乘(n! = n * (n-1)!和斐波那契数(F(n) = F(n-1) + F(n - 2))。对于阶乘,基本情况为0! = 1,对于斐波那契数,有两个基本情况, F(0) = 0F(1) = 1


一些松散的结局:

  • 在偶数情况下使用相同的幂函数进行平方的问题是,它将导致3^2处的无限循环。如果我们在不改变偶数规则的情况下进行评估,则会得到(3^1)^2。如我们所见,3^13,所以我们将回到3^2。这告诉我们3^2等于3^2,这是正确的,但没有用。另一种选择是使n = 2之后的n = 0成为另一种基本情况,但是这种方式更简洁。
  • 在添加基本案例并使用squared而不是^2来处理偶数之后,最终算法是:
    • 如果n0,则x^n = 1
    • 否则,如果n是偶数,则x^n = (x^(n/2)) squared
    • 否则n是奇数,所以x^n = x*(x^(n-1))
  • 要将其放入代码中,请参见Brendan's answer。由于查看调用堆栈很有用,因此以下是该答案的第二版代码中发生的事情,与我描述的非常接近:
    • power(3, 5)执行return 3 * power(3, 4);
    • power(3, 4)执行temp = power(3, 2);
    • power(3, 2)执行temp = power(3, 1);
    • power(3, 1)执行return 3 * power(3, 0);
    • power(3, 0)返回1power(3, 1)
    • power(3, 1)计算3 * 1并将3返回到power(3, 2)
    • power(3, 2)执行return temp * temp,评估3 * 3,然后将9返回到power(3, 4)
    • power(3, 4)执行return temp * temp,评估9 * 9,然后将81返回到power(3, 5)
    • power(3, 5)计算3 * 81并返回243
  • 在编程中,尤其是在类似C语言的编程中,我经常看到**用于求幂而不是^,因为^意味着其他含义(按位xor)。

答案 2 :(得分:0)

#include <stdio.h>

int power(int n1, int n2);

int main()
{
    int base, powerRaised, result;

    printf("Enter base number: ");
    scanf("%d",&base);

    printf("Enter power number(positive integer): ");
    scanf("%d",&powerRaised);

    result = power(base, powerRaised);

    printf("%d^%d = %d", base, powerRaised, result);
    return 0;
}

int power(int base, int powerRaised)
{
    if (powerRaised != 0)
        return (base*power(base, powerRaised-1));
    else
        return 1;
}