前几天在CSDN上看到一个帖子:求X开Y次方的算法。我当时就回复了,当时的想法和做法还不完善,这几天我自己也一直在琢磨这个问题,同时把当时的想法和代码完善了一下,如下:
#include <math.h>
#include <stdio.h>
/*注意:此函数的默认前提是x>0,y>0且为整数,
*函数中的精度参数ep并不表示结果的精度而是表示判断中间计算结果的精度。
*函数的返回值是x开y次方的结果。
*该算法致命的弱点是:如果y很大,那么由于这个算法是个双层
*循环,会导致极大的循环量,非常耗时,计算出结果也会很慢很慢。
*必须找出一种更好的验证方法!
*/
/*我自己发现可能有一种方法有可能减少循环次数:
*将y分解因数,假设y=a*b*c,那么x开y次方,就可以将x先开
*a次方,再将结果开b次方,再将结果开c次方。a,b,c的值越接近,
*那么减少的循环次数会越多,效率会越高。但是对于很大的质数,这个
*办法就无效了。可能存在的另外一个问题是,由于几次求值,可能导致
*误差积累,使结果偏差很大。
*/
double x_sq_y(double x,unsigned int y)
{
double s,root,b;
double ep=1e-8;
double ji;
int small=0,big=0;
unsigned int i;
if(x<1.0) {s=x;b=1;root=(b+s)/2;ep=x*1e-8;}
else {s=0;root=x/y;b=0;}
/*这里对浮点数使用 != 号,是因为运算到了最后root和s,或者
*root和b在double类型范围内计算机已经无法分辨其差别,所以
*肯定会相等。
*/
while(root != s && root != b){
ji=1.0;
i=0;
while(i < y){
ji*=root;
i++;
/*使用除法是为了防止乘法可能出现的溢出,这里的判断也是为了
*不做无效的乘法计算,减少循环次数。
*/
if((root>=1 && i <y &&((x+ep)/ji<root)) ||
(root< 1 && i==y && ji>x+ep)) {
big=1;
b=root;
if(small==0) root/=2;
else root=(b+s)/2;
break;
}
if((root>=1 && i==y && ji < x-ep) ||
(root<1 && i <y &&(x-ep)/ji>root)) {
small=1;
s=root;
if(big==0) root*=2;
else root=(b+s)/2;
break;
}
/*执行到了这里而且i==y,那么root必然就是要求的结果。*/
if(i==y) return root;
}
}
return root;
}
int main()
{
double x;
unsigned int y;
int n;
printf("这个程序计算x开y次方的结果,你需要输入x和y的值。/n");
printf("注意:x>0,而且y为自然数。/n");
while(1){
input:
n=scanf("%lf %u",&x,&y);
if(x<=0 || y==0) goto input;
if(n != 2) break;
printf("pow(%G,%lf)=%.8lf/n",x,1.0/y,pow(x,1.0/y));
printf("%G开%d次方结果=%.8lf /n",x,y,x_sq_y(x,y));
}
return 0;
}
同时我还在思考,到底有什么方法可以更高效的将X开Y次方呢?
#include <math.h>
#include <stdio.h>
/*注意:此函数的默认前提是x>0,y>0且为整数,
*函数中的精度参数ep并不表示结果的精度而是表示判断中间计算结果的精度。
*函数的返回值是x开y次方的结果。
*该算法致命的弱点是:如果y很大,那么由于这个算法是个双层
*循环,会导致极大的循环量,非常耗时,计算出结果也会很慢很慢。
*必须找出一种更好的验证方法!
*/
/*我自己发现可能有一种方法有可能减少循环次数:
*将y分解因数,假设y=a*b*c,那么x开y次方,就可以将x先开
*a次方,再将结果开b次方,再将结果开c次方。a,b,c的值越接近,
*那么减少的循环次数会越多,效率会越高。但是对于很大的质数,这个
*办法就无效了。可能存在的另外一个问题是,由于几次求值,可能导致
*误差积累,使结果偏差很大。
*/
double x_sq_y(double x,unsigned int y)
{
double s,root,b;
double ep=1e-8;
double ji;
int small=0,big=0;
unsigned int i;
if(x<1.0) {s=x;b=1;root=(b+s)/2;ep=x*1e-8;}
else {s=0;root=x/y;b=0;}
/*这里对浮点数使用 != 号,是因为运算到了最后root和s,或者
*root和b在double类型范围内计算机已经无法分辨其差别,所以
*肯定会相等。
*/
while(root != s && root != b){
ji=1.0;
i=0;
while(i < y){
ji*=root;
i++;
/*使用除法是为了防止乘法可能出现的溢出,这里的判断也是为了
*不做无效的乘法计算,减少循环次数。
*/
if((root>=1 && i <y &&((x+ep)/ji<root)) ||
(root< 1 && i==y && ji>x+ep)) {
big=1;
b=root;
if(small==0) root/=2;
else root=(b+s)/2;
break;
}
if((root>=1 && i==y && ji < x-ep) ||
(root<1 && i <y &&(x-ep)/ji>root)) {
small=1;
s=root;
if(big==0) root*=2;
else root=(b+s)/2;
break;
}
/*执行到了这里而且i==y,那么root必然就是要求的结果。*/
if(i==y) return root;
}
}
return root;
}
int main()
{
double x;
unsigned int y;
int n;
printf("这个程序计算x开y次方的结果,你需要输入x和y的值。/n");
printf("注意:x>0,而且y为自然数。/n");
while(1){
input:
n=scanf("%lf %u",&x,&y);
if(x<=0 || y==0) goto input;
if(n != 2) break;
printf("pow(%G,%lf)=%.8lf/n",x,1.0/y,pow(x,1.0/y));
printf("%G开%d次方结果=%.8lf /n",x,y,x_sq_y(x,y));
}
return 0;
}
同时我还在思考,到底有什么方法可以更高效的将X开Y次方呢?