Algorithm 哪个递归调用最有效?
因此,我有一个任务,要编写一个方法,借助递归求x的n次方。基本情况是,如果n=0,那么x^n=1。 如果n是奇数,那么x^n=x*(x^2)^((n-1)/2)。 如果n是偶数,那么x^n=(x^2)^(n/2)。不确定您是否需要此信息,但只是为了以防万一而添加 我的问题是,我有两种方法来使用递归调用,但我不确定哪一种是“最好的”还是最有效的 方法如下:Algorithm 哪个递归调用最有效?,algorithm,recursion,big-o,Algorithm,Recursion,Big O,因此,我有一个任务,要编写一个方法,借助递归求x的n次方。基本情况是,如果n=0,那么x^n=1。 如果n是奇数,那么x^n=x*(x^2)^((n-1)/2)。 如果n是偶数,那么x^n=(x^2)^(n/2)。不确定您是否需要此信息,但只是为了以防万一而添加 我的问题是,我有两种方法来使用递归调用,但我不确定哪一种是“最好的”还是最有效的 方法如下: public double findNthPowerOfXV2(int n, double x){ if(n == 0){
public double findNthPowerOfXV2(int n, double x){
if(n == 0){
return 1;
}else if(!(n % 2 == 0)){
return x * findNthPowerOfXV2(((n-1)/2), x) * findNthPowerOfXV2(((n-1)/2), x);
//return x * findNthPowerOfXV2(((n-1)/2), x * x) also works, but not sure which is best
}else{
return findNthPowerOfXV2(n/2, x) * findNthPowerOfXV2(n/2, x);
//return findNthPowerOfXV2(n/2, x * x) also works, but not sure which is best
}
}
Date start2 = new Date();
int rounds2 = 0;
double time2;
Date stop2;
do {
main.findNthPowerOfXV2(5000, 1.001);
stop2 = new Date();
++rounds2;
}while(stop2.getTime() - start2.getTime() < 1000);
time2 = (double) stop2.getTime() - start2.getTime() / rounds2;
System.out.println(time2);
在我看来,注释掉的代码行看起来更好、更正确。当我说注释掉的行使方法执行更少的循环时,我不确定我是否错了?
但是,当我测试完成该方法所需的时间时,未注释掉的行比注释掉的行快。即使它有两个递归调用,我猜这会导致更多的循环
这是我用来测试方法时间使用情况的代码:
public double findNthPowerOfXV2(int n, double x){
if(n == 0){
return 1;
}else if(!(n % 2 == 0)){
return x * findNthPowerOfXV2(((n-1)/2), x) * findNthPowerOfXV2(((n-1)/2), x);
//return x * findNthPowerOfXV2(((n-1)/2), x * x) also works, but not sure which is best
}else{
return findNthPowerOfXV2(n/2, x) * findNthPowerOfXV2(n/2, x);
//return findNthPowerOfXV2(n/2, x * x) also works, but not sure which is best
}
}
Date start2 = new Date();
int rounds2 = 0;
double time2;
Date stop2;
do {
main.findNthPowerOfXV2(5000, 1.001);
stop2 = new Date();
++rounds2;
}while(stop2.getTime() - start2.getTime() < 1000);
time2 = (double) stop2.getTime() - start2.getTime() / rounds2;
System.out.println(time2);
datestart2=新日期();
int rounds2=0;
双时间2;
日期停止2;
做{
main.findNthPowerOfXV2(5000,1.001);
stop2=新日期();
++圆形2;
}while(stop2.getTime()-start2.getTime()<1000);
time2=(双精度)stop2.getTime()-start2.getTime()/rounds2;
System.out.println(time2);
当我使用未注释掉的行时,我得到1.597890920672E12
当我使用注释掉的行时,我得到1.597915039728E12
那么,哪一个更适合使用呢?我正在寻找一个解释,哪一个更好地使用和为什么。提前感谢:)您认为注释掉的版本原则上更有效,这是正确的。考虑在计算<代码> 2 ^ 4 < /代码>时,我们做了多少个调用。 首先是低效的版本:
- 2^4
- 2^2
- 2^1
- 2^0
- 2^0
- 2^1
- 2^0
- 2^0
- 2^2
- 2^1
- 2^0
- 2^0
- 2^1
- 2^0
- 2^0
- 2^4
- 2^2
- 2^1
- 2^0
如果我们加倍n
,这个树的大小会加倍,所以这个算法是O(n)
。另一种方法是假设它对于递归调用是正确的,然后证明它对于基调用也是正确的。在本例中,我们进行两个递归调用,每个递归调用执行n/2
操作(根据假设),因此基本调用执行2*n/2=n
操作
现在是高效版本:
- 2^4
- 2^2
- 2^1
- 2^0
- 2^0
- 2^1
- 2^0
- 2^0
- 2^2
- 2^1
- 2^0
- 2^0
- 2^1
- 2^0
- 2^0
- 2^4
- 2^2
- 2^1
- 2^0
这里,如果我们将n
的值加倍,树的大小将增加1,这就是O(logn)
我将把严格的证据留给读者。在这两种情况下,我都忽略了n
的奇数值,因为它们在大O方案中并不重要
您无法测量这种差异的事实可能更能说明您的基准测试代码:
- 构造一个
对象调用内存管理器,可能触发垃圾回收器,并可能执行系统调用从操作系统获取当前时间,因此它可能比您正在测试的实际函数花费更长的时间新日期
- Java运行时(我假设这是Java)使用了一个即时编译器,它只在函数被多次调用时才启动,因此在它第一次运行时,结果并不具有代表性
- 也许编译器足够聪明,可以认识到函数是纯函数,并且您正在使用相同的参数执行两个调用,因此它可能不像看上去那么低效
新日期()
所需的时间,该日期可能比您自己的函数大。更好的做法是进行固定次数的迭代,调整到只需几秒钟即可运行;然后重复多次,因为第一次代码可能还没有被JIT。标杆很难!