问题——如何求x^n(注: n为整数,x和n不可同时为零)
力扣(50题)
分析:首先讨论可能出现的几种情况
1.x==0&&n<=0,这种情况我们抛异常(math error)
2.x!=0&&n==0,结果为1
3.x==0&&n>0,结果为0
4.其他情况我们正常计算
分析完了,代码如下
public double power(double x,int n){
//这个合法判断应该放在这个函数
if(x==0&&n<=0){
throw new RuntimeException("math error!");
}
//这里需要用一个long类型变量,因为int类型最小的数的绝对值比最大值要大1
long num=n;
return num>=0? myPower(x,num):(1.0/myPower(x,-num));
}
//保证n为非负整数
private double myPower(double x,long n){
if(n==0){
return 1.0;
}
double ans=1.0;
while(n>0){
ans=ans*x;
n--;
}
return ans;
}
这种朴素的求幂方法o(n)在n的规模很大时会time out!
分治法
显然数学上而言,x^n=x^(n/2)*x^(n/2)
举个例子,例如我们要求3^65,朴素的算法要计算65次乘法运算,而用上面的的公式分解显然会有=,而不断划分下去,很快就能降到
3=1*1*3,对于指数为n的只要进行logn次乘法
代码实现
public double powerByRecursion(double x,long n){
//这个判断应该在n==0上面
if(x==0&&n>0){
return 0.0;
}
if(n==0){
return 1.0;
}
//直观的查看递归了多少次
System.out.println("函数执行了");
double ans=powerByRecursion(x,n>>1);
return (n&1)==1? ans*ans*x:ans*ans;
}
运行效果如下
使用double来存储计算幂显然不够用,不过可以看到执行效率大大提高了
以下补充快速幂算法的迭代版本
public double quickPower(double x,long n){
if(n==0){
return 1.0;
}
double ans=1.0;
while(n>0){
//如果当前位不为0
if((n&1)==1){
ans=ans*x;
}
//自乘表示取对应位的值,为下一次循环准备
x=x*x;
n=n>>1;
}
return ans;
}