浮点数误差问题
我们在判断底数x
(double
类型)是否等于0的时候,不能直接写x==0
,这是因为计算机在表示浮点数的时候存在误差。我们不能用等号判断两个小数是否相等。如果两个小数的差的绝对值很小,就可以认为他们相等。具体的原因可以参考这篇文章:浮点型变量的误差问题。
方法一:基础解法
首先考虑会出现错误的情况,当底数base=0时,指数为负数,取倒数后分母为0,所以是一种错误的情况,应该在程序当中给出提示。
其他情况就可以考虑共用一个累乘函数,如果指数是负数,首先将指数取绝对值,再使用累乘函数得到结果。将结果再取倒数就能得到正确的答案。
class Solution {
public:
bool errorFlag=false;
double Power(double base, int exponent) {
errorFlag=false;
double res;
if(exponent<0 && myequal(base,0.0)) {
errorFlag=true; //使用flag与其他正常返回结果0.0的情况区分开
return 0.0;
}
unsigned int exp=(unsigned int)exponent;
if(exponent<0) exp=(unsigned int)-exponent;
res=myPower(base,exp);
if(exponent<0) return 1.0/res;
return res;
}
double myPower(double base,int exp){
double res=1.0; //如果指数exp=0,直接返回1.0,不会进入循环。
for(int i=0;i<exp;i++){
res *= base;
}
return res;
}
bool myequal(double num1,double num2){
if((num1-num2)<0.0000001 && (num1-num2)>-0.0000001) return true;
else return false;
}
};
但是这种解法在LeetCode上是行不通的,因为它的时间复杂度为O(n),一个测试用例
1.00000
2147483647
就能让程序超出时间限制。
方法二:快速幂法
快速幂法可将时间复杂度降为O(logn),解法参考自:剑指 Offer 16 题目解析。
推导过程:
x
n
=
x
n
/
2
×
x
n
/
2
=
(
x
2
)
n
/
2
x^n = x^{n/2} ×x^{n/2} = (x^2)^{n/2}
xn=xn/2×xn/2=(x2)n/2
令 n/2 为整数,则n需要分奇偶数讨论:
x n = { ( x 2 ) n / / 2 , if n is even x ( x 2 ) n / / 2 , if n is odd x^n= \begin{cases} (x^2)^{n//2}, & \text {if $n$ is even} \\ x(x^2)^{n//2}, & \text{if $n$ is odd} \end{cases} xn={(x2)n//2,x(x2)n//2,if n is evenif n is odd
设res=1.0,则初始状态为 x n = x n × r e s x^n =x^n × res xn=xn×res
下面以 3^5为例进行推导:
循环 | x n × ( r e s ) x^n × (res) xn×(res) |
---|---|
第0轮 | 3 5 × 1 3^5 × 1 35×1 |
第1轮 | 9 2 × 1 × 3 9^2 × 1 × 3 92×1×3 |
第2轮 | 8 1 1 × 1 × 3 81^1 × 1 × 3 811×1×3 |
第3轮 | 656 1 0 × 1 × 3 × 81 6561^0 × 1 × 3 × 81 65610×1×3×81 |
推导可得
3
5
=
656
1
0
×
1
×
3
×
81
=
1
×
1
×
3
×
81
3^5 =6561^0 × 1 × 3 × 81 =1 × 1 × 3 × 81
35=65610×1×3×81=1×1×3×81
class Solution {
public:
double myPow(double x, int n) {
//0的任何次方都为0;这里不考虑错误情况直接返回;
if(myEqual(x,0.0)) {
return 0.0;
}
double res=1.0;
long b=n;
if(b<0){
x=1.0/x;
b=-b;
}
while(b>0){
if(b & 1) res *=x; //如果指数为奇数
x*=x; //
b >>= 1; //除2向下取整操作
}
return res;
}
bool myEqual(double num1,double num2){
if((num1-num2)<0.0000001 && (num1-num2)>-0.00000001) return true;
else return false;
}
};
复杂度分析:
- 时间复杂度:O(logn)
- 空间复杂度:O(1)