本文通过比较对数字指定误差的求开平方不同算法实现之间的效率比较,来使程序入门者对不同算法的性能差距有直观的印象,并且对算法的作用有深刻的体会。
算法一(暴力遍历法):
/**
* 求开方
* @param source 被开方数,大于等于0
* @param deviation 误差范围
* @return
*/
public static double sqrt(double source,double deviation) {
if(source < 0 || deviation <0) {
throw new RuntimeException("don't transmit negtive");
}
long count = 1;//统计循环执行次数
double result = 0;
while ((result + 1) * (result + 1) < source) {
count++;
result++;
}
while ((result + deviation) * (result + deviation) < source ) {
count++;
result += deviation;
}
System.out.println("sqrt total count:" + count);
return result;
}
本算法是将计算结果从0开始一点一点增加并进行试探,直到接近真实结果误差范围内。
算法二(步长调整法):
/**
* 求开方 优化,步长调整
* @param source 被开方数,大于等于0
* @param deviation 误差范围
* @return
*/
public static double sqrt1(double source,double deviation) {
if(source < 0 || deviation <0) {
throw new RuntimeException("don't transmit negtive");
}
long count = 1;//统计循环执行次数
double result = 0;
int stepI = 2;
double stepD = deviation;
long stepCount = 1;
while ((result + 1) * (result + 1) < source) {
count++;
stepCount++;
if(stepCount%3==0){ //加快结果累计,调整步长
if((result + stepI) * (result + stepI) < source) {
result += stepI;
stepI++;
continue;
} else {
stepI--;
}
}
result++;
}
stepCount = 0;
while ((result + deviation) * (result + deviation) < source ) {
count++;
stepCount++;
if(stepCount%3==0){ //加快结果累计,调整步长
if((result + stepD) * (result + stepD) < source) {
result += stepD;
stepD+=deviation;
continue;
} else {
stepD-=deviation;
}
}
result += deviation;
}
System.out.println("sqrt1 total count :" + count);
return result;
}
本算法每当执行循环三次时调整一次步长,读者可以自行定制更加高效的调整步长策略。
算法三(二分法):
/**
* 求开方 优化,二分法
* 该方法效率明显比前两个方法快的多
* @param source 被开方数,大于等于0
* @param deviation 误差范围
* @return
*/
public static double sqrt2(double source,double deviation) {
if(source < 0 || deviation <0) {
throw new RuntimeException("don't transmit negtive");
}
long count = 1;//统计循环执行次数
double result = 0;
double head = source;
double tail = 0;
while(true) {
count++;
if(((head+tail)/2) * ((head+tail)/2) < source) {
tail = (head+tail)/2;
} else {
head = (head+tail)/2;
}
result = (head+tail)/2;
if((result + deviation)*(result + deviation) >= source &&
(result - deviation)*(result - deviation) <= source) {
break;
}
}
System.out.println("sqrt2 total count:" + count);
return result;
}
本算法为典型的二分法,算法的原理如下:将0和source分别作为结果的初始下界和上界,将下界和上界的平均值与真实结果比较并调整结果的下界或者上界,直至结果位于真实结果的误差范围内。
上面已经给出了算法的实现,算法可能的难点是怎么判断算出的值是否在误差范围内,读者需要注意这点。下面通过几组数据简单的比较一下不同算法实现之间的效率差别:
上图中,我们测试了三组数据,从标红的数据可以看出,算法二明显优于算法一,而算法三明显优于算法二。当执行第三组数据时,算法一甚至要等一小会儿才能执行完,而算法三却马上就能得到结果,而且算法三随着问题规模的扩大执行次数却增长很慢,而这只是简单的几行代码改进的结果。从这个算法问题中我们可以看到不同算法之间效率的巨大差距,由此不难体会在许多算法应用场合下算法巨大的威力。