查看以下代码以查找X ^ y。
/* Find exponent in logarithmic complexity */
int findPower(int base, exponent){
if( 1 == exponent ) return base;
return (findPower(base, exponent/2)*findPower(base, exponent-exponent/2));
}
int main(int argc, char *argv[]){
if(argc < 3) {
printf("Usage :: logpow baseNumber power\n");
return -1;
}
printf("%s ^ %s = %d\n", argv[1], argv[2], findPow( atoi(argv[1]),
atoi(argv[2])) );
return 0;
}
分析显示这具有theta(log(n))的复杂性。 但是我用它来测量时间,这是结果
Run 1: (calculating 1^500_million)
user-lm Programming # time ./a.out 1 500000000
1 ^ 500000000 = 1
real 0m5.009s
user 0m5.000s
sys 0m0.000s
Run 2: (calculating 1^1_Billion)
user-lm Programming # time ./a.out 1 1000000000
1 ^ 1000000000 = 1
real 0m9.667s
user 0m9.640s
sys 0m0.000s
Run 3: (calculating 1^2_Billion)
user-lm Programming # time ./a.out 1 2000000000
1 ^ 2000000000 = 1
real 0m18.649s
user 0m18.630s
sys 0m0.000s
从上面我们可以看到实际时间复杂度具有线性行为而不是对数!
复杂性如此巨大差异的原因是什么?
问候,
微内核
答案 0 :(得分:11)
您实际上正在调用每次调用的2个函数调用。递归树将是高度为log(exponent)
的二叉树,因此其中的节点数将为2^log(exponent) == exponent
。总的来说它变成了线性算法。您可以像这样重写它以获得更好的性能:
int findPower(int base, int exponent){
if( 0 == exponent ) return 1;
int temp = findPower(base, exponent/2);
if(exponent % 2 == 0) return temp * temp;
return temp * temp * base;
}
诀窍是,您必须存储findPower(base, exponent/2)
的值才能获得对数的复杂性。递归树仍然具有高度log(exponent)
,但每个节点现在只有一个子节点,因此会有log(exponent)
个节点。如果实际上调用它两次,它将降低性能甚至超过线性性能。如果您已经拥有它,则无需第二次计算相同的值!
正如@David Schwartz指出的那样,如果exponent
加倍,代码中的调用次数将加倍。但是,当您保存这些值时,exponent
加倍会使一个更多的呼叫。
答案 1 :(得分:4)
您的分析不正确,为O(N)。
当你将N从10亿提高到20亿时,你必须在10亿上进行两次电力操作。因此,加倍N会使需要完成的工作增加一倍。那是O(N)。
答案 2 :(得分:2)
您的算法复杂性有正式表示:
T(n) = 2T(n/2) + c
n
是指数。这给了
T(n) = Theta(n)
分析不正确。