数值的整数次方

简介: 数值的整数次方

前言


在JavaScript中有一个库函数(Math.pow())可以对一个数进行次方运算,本文将实现一个类似pow功能的函数,欢迎各位感兴趣的开发者阅读本文。


实现思路


第一眼看到这个问题,有些开发者可能思考几秒钟,觉得这很简单嘛。直接遍历次方数,将底数与前一次的计算结果相乘即可,直接一把梭🤓,很快就写完了代码,如下所示:


/**
   * 计算一个数的次方
   * @param base 底数
   * @param exponent 指数
   */
  public power(base: number, exponent: number): number {
    let result = 1;
    for (let i = 1; i <= exponent; i++) {
      result *= base;
    }
    return result;
  }


细心的开发者可能已经发现了这段代码的不足之处,上述代码只考虑了指数是正数的情况,当输入的指数为小于1的时候上述代码就计算错误了🌝


640.png

                              image-20211114225904657


全面考虑的解法


接下来,我们把指数为负数和0时的情况考虑进去,来捋一下实现思路:


  • 当指数为负数的时候,需要对指数求绝对值,算出次方的结果之后再取倒数
  • 当指数为0时,我们就要考虑两种情况:
  • 当底数为0且指数为负数时,就会出现对0求倒数,会导致程序运行出错,需要进行容错处理,将错误信息告知调用者
  • 当底数为0且指数为0时,这在数学上是没有意义的,此处我们将结果返回0或1都可以


我们将上述思路转化为代码,如下所示:


/**
   * 计算一个数的次方
   * @param base 底数
   * @param exponent 指数
   */
  public power(base: number, exponent: number): number | string {
    // 求出指数对绝对值
    const absExponent = Math.abs(exponent);
    // 程序会出错
    if (base === 0 && exponent < 0) {
      return "参数错误: 0的负次方不能进行计算";
    }
    // 当底数和指数都为0时,在数学上是没意义的
    if (base === 0 && exponent === 0) {
      return 0;
    }
    let result = 1;
    for (let i = 1; i <= absExponent; i++) {
      result *= base;
    }
    // 指数小于0, 对计算出的结果求倒数
    if (exponent < 0) {
      result = 1 / result;
    }
    return result;
  }


我们将各种情况都考虑进去,作为参数传入上述代码,观察下运行结果,如下所示:


640.png

                                 image-20211114235913960


最优解


上述解法已经满足了题目要求,但是并不是最优解法,接下来,我们来看一种更好的解法。


上述代码中循环计算底数的指数次方代码可以拆分成一个函数,如下所示:


/**
   * 求底数的指数次方
   * @param base
   * @param exponent
   */
  private static powerWithExponent(base: number, exponent: number) {
    let result = 1;
    for (let i = 1; i <= exponent; i++) {
      result *= base;
    }
    return result;
  }


拆分后,我们考虑这样一个场景:


当指数为32时,就需要在上述函数的循环中做31乘法。然而,我们的目标就是求出一个数字的32次方,如果我们已经知道了它的16次方,那么只要在16次方的基础上再平方一次就可以了。而16次方是8次方的平方。


以此类推,我们求32次方只需要做5次乘法:


  • 先求平方
  • 在平方的基础上求4次方
  • 在4次方的基础上求8次方
  • 在8次方的基础上求16次方
  • 在16次方的基础上求32次方


思考到这里,我们设要求的次方为n,那么:


  • 当n为偶数时,可以拆分为n/2 * n/2
  • 当n为奇数时,可以拆分为(n-1)/2 * (n-1)/2


乘式两边计算出结果后,仍然可以对结果应用上述规则进行计算,直至n为0或1,总结成公式后,如下图所示:


640.png

                                 image-20211115211445909


实现代码


有了公式后,我们很快就能想到可以用递归来解决这个问题,我们来画一下递归栈,如下图所示:


640.png

                          image-20211115233901255


思路捋清楚后,就可以愉快的进入编码环节了🤓,完整代码如下所示:


export default class IntegerPower {
  /**
   * 计算一个数的次方
   * @param base 底数
   * @param exponent 指数
   */
  public power(base: number, exponent: number): number | string {
    // 求出指数对绝对值
    const absExponent = Math.abs(exponent);
    // 程序会出错
    if (base === 0 && exponent < 0) {
      return "参数错误: 0的负次方不能进行计算";
    }
    // 当底数和指数都为0时,在数学上是没意义的
    if (base === 0 && exponent === 0) {
      return 0;
    }
    // 进行次方运算
    let result = this.powerWithExponent(base, absExponent);
    // 指数小于0, 对计算出的结果求倒数
    if (exponent < 0) {
      result = 1 / result;
    }
    return result;
  }
  /**
   * 求底数的指数次方
   * @param base 底数
   * @param exponent 指数
   */
  private powerWithExponent(base: number, exponent: number): number {
    // 递归终止条件
    if (exponent === 0) {
      // 非0数的0次方值为1
      return 1;
    }
    if (exponent === 1) {
      // 任意数的0次方为其本身
      return base;
    }
    // 递归对指数/2,计算结果
    // 用右移运算符代替除以2运算
    let result =
      this.powerWithExponent(base, exponent >> 1) *
      this.powerWithExponent(base, exponent >> 1);
    // 指数为奇数时,result就为result乘以底数
    if (exponent % 2 === 1) {
      result *= base;
    }
    return result;
  }
}


上述代码中,我使用了右移运算符来代替除法运算,这样可以提升代码的执行速度。对此不了解的开发者请移步我的另一篇文章:二进制中一的个数-右移运算符[1]

对递归不熟悉的开发者,请移步:递归的理解与实现[2]


编写测试用例


接下来,我们将各种边界条件都考虑进去,验证下上述代码能否正确执行。


import IntegerPower from "../IntegerPower.ts";
const powerHandler = new IntegerPower();
const result1 = powerHandler.power(5, 6);
const result2 = powerHandler.power(3, -4);
const result3 = powerHandler.power(0, 0);
const result4 = powerHandler.power(0, 3);
const result5 = powerHandler.power(0, -3);
console.log(result1);
console.log(result2);
console.log(result3);
console.log(result4);
console.log(result5);


运行结果如下所示:


640.png

                                       image-20211115225812545


项目代码


文中的示例代码请移步:


  • IntegerPower.ts[3]
  • integerPower-test.ts[4]


写在最后


至此,文章就分享完毕了。


我是神奇的程序员,一位前端开发工程师。


如果你对我感兴趣,请移步我的个人网站[5],进一步了解。


  • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
  • 文中链接可从文末参考资料中获取🤗


相关文章
|
2月前
取一个整数 a 从右端开始的 4~7 位
取一个整数 a 从右端开始的 4~7 位。
22 1
|
3月前
|
算法 Python
计算32位二进制整数中1的个数(包括负数补码)
计算32位二进制整数中1的个数(包括负数补码)
28 0
【剑指offer】-数值的整数次方-12/67
【剑指offer】-数值的整数次方-12/67
|
8月前
wustojc1006求2个整数的和
wustojc1006求2个整数的和
29 0
|
10月前
剑指offer 15. 数值的整数次方
剑指offer 15. 数值的整数次方
37 0
35.数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方
34 0
35.数值的整数次方
05:整数大小比较
05:整数大小比较
89 0
|
存储 Java
从0.2+0.4不等于0.6说浮点数
从0.2+0.4不等于0.6说浮点数,浮点数我一直心存疑惑。
121 0
从0.2+0.4不等于0.6说浮点数
求浮点数的整数次幂
/** * 求浮点数的整数次幂 * pow(0.99, 365) = 0.025 (每天做少一点,每年积累的仅有40分之一) * pow(1.01, 365) = 37.
1329 0