《剑指offer》面试题16:数值的整数次方

更多剑指offer面试习题请点击: 《剑指offer》(第二版)题集目录索引

题目:
  实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。


解题思路:
  很多人可能一开始看到不需要考虑大数问题,就觉着这题很简单,快速的写下下面的代码:
< code >

double power(double base, int exponent)
{
    double result = 1.0;
    int i = 0;
    for (i = 0; i <= exponent; i++)
    {
        result *= base;
    }

    return result;
}

  这段代码对于底数、指数均为正数的情况下是没毛病的,但如果输入的指数小于等于零,那就会出问题了。这段代码完全没考虑到这些特殊情况。

  当指数exponent为负数时,我们先对指数求绝对值abs_exponent,计算出底数的abs _exponent次方,然后对结果求倒。但当底数为0时,对0求倒就会出现错误,这个时候就要把错误告诉函数调用者,一般可以使用以下三种方法。

项目 优 点 缺点
返回值 和系统API一致 不能方便的使用计算结果
全局变量 能够方便的使用计算结果 用户可能会忘记检查全局变量
异常 可以为不同的出错原因定义不同的异常类型,逻辑清晰明了 有些语言不支持异常,抛出异常时对性能有负面影响

  这里我们采用第二种方法:全局变量。设置一个int型全局变量invalid_input,初始值是0,当出错时置为1,这样可以很方便的显示出错的情况。写到这可能大家觉的这个函数差不多了,边界值啥的都考虑到了,但我们不能忘了一个最重要的东西——效率。

注意: pow函数和我们平常理解的数学有点差异。比如:

  • 底数为0,指数不能为负数(不能对0求倒)
  • 底数、指数均为0,最后结果是1。

在数学里面0^0是未定式;
但在离散里面0^0=1;
在计算机中,规定了0^0=1;


位运算法

解题思路:

  1. 写出指数的二进制表达,例如11表达为二进制1011。
  2. 举例:10^1011 = 10^0001*10^0010*10^1000。
  3. 通过&1和>>1来逐位读取1011,为1时将该位代表的乘数累乘result到最终结果。 每位移一次都要翻倍

< code >

double power_unsigned_exponent(double base, unsigned int exponent) //递归
{
    if (0 == exponent)
        return 1;
    if (1 == exponent)
        return base;

    double result = 1.0;
    double tmp = base;
    while (exponent != 0)
    {
        if ((exponent & 1) == 1) //  当前指数的二进制最低位为不为1
        {
            result *= tmp;        //  就计算一个乘积
        }

        tmp *= tmp;          // 翻倍
        exponent >>= 1;      // 右移一位
    }
    return result;
}

递归法

解题思路:
  我们要计算一个数的32次方,如果已经知道他的16次方的值了,那就只用把他的16次方的值平方一次就可以了。而16次方又是8的平方,这样一次类推,求一个数的32次方只要进行做5次乘法就够了。
  先求平方,在平方的基础上求4次方,在4次方的基础上求8次方,在8次方的基础上求16次方,在16次方的基础上求32次方。

< code >

/*错误标识码,1代表输入有误,0代表输入正确*/
int invalid_input = 0;

/*判断两个double类型的数据是否相等*/
int equal(double number1, double number2) 
{
    if ((number1 - number2 < 0.0000001) && (number1 - number2 > -0.0000001))
        return 1;
    else
        return 0;
}

double power_unsigned_exponent(double base, unsigned int exponent) //递归
{
    if (0 == exponent)
        return 1;
    if (1 == exponent)
        return base;

    double result = power_unsigned_exponent(base, exponent >> 1);
    result *= result;
    /*用 >> 代替除2,用&代替判断奇数%2,提高效率*/

    if (exponent & 1)
        result *= base;

    return result;
}


double power(double base, int exponent)
{
    double result = 0.0;
    unsigned int abs_exponent = (unsigned int)exponent;

    /*底数为0且指数为负数情况下,直接返回0.0,但此时invalid_input置为1,代表输入的数据有误*/
    if (equal(0.0, base) && exponent < 0)
    {
        invalid_input = 1;
        return 0.0;
    }

    /*先把负指数转换成正的*/
    if (exponent < 0) 
        abs_exponent = (unsigned int)(-exponent);

    result = power_unsigned_exponent(base, abs_exponent);

    /*指数为负,对最后结果求倒*/
    if (exponent < 0) 
        result = 1.0 / result;

    return result;
}
//==========================测试代码===============================

void test(char* testName, double base, int exponent, double expected_result, int expected_flag)
{
    double result = power(base, exponent);

    if (equal(result, expected_result) && invalid_input == expected_flag)
        printf("%s passed\n", testName);
    else
        printf("%s FAILED\n", testName);
}

int main()
{
    /*底数、指数均为正数*/
    test("test1", 3, 3, 27, 0);

    /*底数为负数,指数为正数*/
    test("test2", -2, 2, 4, 0);

    /*底数为正数,指数为负数*/
    test("test3", 2, -2, 0.25, 0);

    /*底数为正数,指数为0*/
    test("test4", 2, 0, 1, 0);

    /*底数、指数均为0*/
    test("test5", 0, 0, 1, 0);

    /*底数为0,指数为正数*/
    test("test6", 0, 2, 0, 0);

    /*底数为0,指数为负数*/
    test("test7", 0, -2, 0, 1);

    system("pause");
    return 0;
}

测试结果:
《剑指offer》面试题16:数值的整数次方_第1张图片

你可能感兴趣的:(剑指offer面试题)