Recursion 难以理解大的Oh复杂性
这不是家庭作业,而是课堂上的练习题。我知道没有解决办法。我想看看我做得对不对。另外,我如何发现时间复杂性?下面是我在这个问题的底部的解决方案 确定以下程序片段的大复杂性 (一)Recursion 难以理解大的Oh复杂性,recursion,big-o,time-complexity,Recursion,Big O,Time Complexity,这不是家庭作业,而是课堂上的练习题。我知道没有解决办法。我想看看我做得对不对。另外,我如何发现时间复杂性?下面是我在这个问题的底部的解决方案 确定以下程序片段的大复杂性 (一) void sum1(int n){ sum1=0; 对于(i=1;i)让我们考虑一个你已经解决的问题: void sum2(int n){ sum2 = 0; for(i=1; i<=n; i++) for (j=1; j<=i; j++) sum
void sum1(int n){
sum1=0;
对于(i=1;i)让我们考虑一个你已经解决的问题:
void sum2(int n){
sum2 = 0;
for(i=1; i<=n; i++)
for (j=1; j<=i; j++)
sum2++;
}
让我们考虑3)
在考虑递归之前,让我们先看看一般情况下的成本(注释R
):
然后按照输入大小的一半进行两次递归-因此对于A
我们现在得到:
A(1) = 0
A(n) = n(n+1)/2 + 2 * A(n/2) n > 1
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * A(n/4))
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * ((n/4)((n/4)+1)/2 + 2 * A(n/8)))
= ...
= n(n+1)/2 + sum i from 1 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )
= sum i from 0 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )
递归发生x
次,即直到达到中断条件B
,并且n==1
。对于n:=2^k
,这完全发生在k
步骤之后-通常我们使用x:=log2(n)
当n
趋于无穷大时。从这里可以得到一个封闭形式的解
请注意,有一些技巧使这更容易:因为我们对计算作业的确切数量不感兴趣,我们可以将n(n+1)/2
简化为n^2
,因为它们都在O(n^2)
中。您可以求解确切的数量或近似值A(n)=n^2+2*A(n/2)< /代码>,它们都在同一个复杂类中。让我们考虑一个你已经解决的问题:
void sum2(int n){
sum2 = 0;
for(i=1; i<=n; i++)
for (j=1; j<=i; j++)
sum2++;
}
让我们考虑3)
在考虑递归之前,让我们先看看一般情况下的成本(注释R
):
然后按照输入大小的一半进行两次递归-因此对于A
我们现在得到:
A(1) = 0
A(n) = n(n+1)/2 + 2 * A(n/2) n > 1
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * A(n/4))
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * ((n/4)((n/4)+1)/2 + 2 * A(n/8)))
= ...
= n(n+1)/2 + sum i from 1 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )
= sum i from 0 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )
递归发生x
次,即直到达到中断条件B
,并且n==1
。对于n:=2^k
,这完全发生在k
步骤之后-通常我们使用x:=log2(n)
当n
趋于无穷大时。从这里可以得到一个封闭形式的解
请注意,有一些技巧使这更容易:因为我们对计算作业的确切数量不感兴趣,我们可以将n(n+1)/2
简化为n^2
,因为它们都在O(n^2)
中。您可以求解确切的数量或近似值A(n)=n^2+2*A(n/2)
,它们都属于同一个复杂级别。@BeyelerStudios迄今为止做得很好;我尽量不重复
我认为您已经掌握了一些基本原则(大部分):
- 对于普通的线性语句,您需要了解语句复杂性的先验知识。基本算术运算和数组访问是O(1)
- 语句序列的顺序是该序列中任何语句的最高顺序
- 决策分支的顺序是任何分支的最高顺序
- 循环的顺序是循环的重复次数乘以循环体的顺序
- 递归调用的顺序是递归的重复次数乘以函数体的顺序
现在来看问题4:
这不是O(n^3):请注意,循环不是嵌套的。每个循环都由一个递增语句O(1)组成。前两个循环是O(sqrt(n));第三个循环使用j的最终值加上一个常量,因此也是O(sqrt(n))。请特别注意,您使用的O(sqrt(n))
问题5:
每个循环都是O(sqrt(n)),但它们现在是嵌套的。这将为您提供sqrt(n)^3或n^(3/2)
未来需要注意的最后一个细节是,sqrt(n)不一定是O(1);它取决于实现。现代芯片上有一个sqrt函数,可以在O(n)时间内处理所有本机整数和浮点。然而,有些数据类型的任意大浮点和整数的实现是O(log(n)).如果您的老师还没有对该操作做出总结性决定,请注意sqrt——以及其他超越函数——潜入循环体。@BeyelerStudios到目前为止做得很好;我尽量不重复
我认为您已经掌握了一些基本原则(大部分):
- 对于普通的线性语句,您需要了解语句复杂性的先验知识。基本算术运算和数组访问是O(1)
- 语句序列的顺序是该序列中任何语句的最高顺序
- 决策分支的顺序是任何分支的最高顺序
- 循环的顺序是循环的重复次数乘以循环体的顺序
- 递归调用的顺序是递归的重复次数乘以函数体的顺序
现在来看问题4:
这不是O(n^3):请注意,循环不是嵌套的。每个循环都由一个递增语句O(1)组成。前两个循环是O(sqrt(n));第三个循环使用j的最终值加上一个常量,因此也是O(sqrt(n))。请特别注意,您使用的O(sqrt(n))
问题5:
每个循环都是O(sqrt(n)),但它们现在是嵌套的。这将为您提供sqrt(n)^3或n^(3/2)
未来需要注意的最后一个细节是,sqrt(n)不一定是O(1);它取决于实现。现代芯片上有一个sqrt函数,可以在O(n)时间内处理所有本机整数和浮点。然而,有些数据类型的任意大浮点和整数的实现是O(log(n))。如果你的老师还没有对这个操作做出总结性的决定,那就要注意sqrt——以及其他超越函数——是否潜入了一个循环体。也许,其中一个答案可以帮助你也许,其中一个答案可以帮助你
void sum4(int n){
sum = 0;
for(i=0; i<sqrt(n)/2; i++)
for(j=1; j<8+i; j++)
for(k=j; k<8+j; k++)
sum++;
}
void sum1(int n){
sum1 = 0; --> 1
for(i=1; i<=n; i++) --> n
for (j=1; j<=n; j++) --> n^(2)
sum1++; --> ?
}
void sum2(int n){
sum2 = 0; --> 1
for(i=1; i<=n; i++) --> n
for (j=1; j<=i; j++) --> n^(2)
sum2++; --> ?
}
float useless(a){
n = a.length; --> ?
if (n==1){
return a[0]; --> ?
}
// let a1, a2 be arrays of size n/2
for (i=0; i <= (n/2)-1; i++){ --> n
a1[i] = a[i]; --> ?
a2[i] = a[n/2 +i]; --> ?
}
for (i=0; i<=(n/2)-1; i++){ ---> n+n = n
for (j = i+1; j<=(n/2) -1; j++){ ---> n^(2)
if(a1[i] == a2[j])
a2[j] = 0; --> 1
}
}
b1 = useless(a1); --> ?
b2 = useless(a2); --> ?
return max(b1,b2); --> ?
}
void sum3(int n) {
sum = 0; --> 1
for(i=0; i<sqrt(n)/2; i++) --> n
sum++; --> ?
for(j=0; j<sqrt(n)/4; j++) --> n^(2)
sum++; --> ?
for(k=0; k<8+j; k++) --> n^(3)
sum++; --> ?
}
void sum4(int n){
sum = 0; --> 1
for(i=0; i<sqrt(n)/2; i++) --> n
for(j=1; j<8+i; j++) --> n^(2)
for(k=j; k<8+j; k++) --> n^(3)
sum++; --> ?
}
void sum2(int n){
sum2 = 0;
for(i=1; i<=n; i++)
for (j=1; j<=i; j++)
sum2++;
}
sum i from 1 to n of [ sum j from 1 to i of (1) ]
= sum i from 1 to n of (i)
= n(n-1)/2
in O(n^2)
float useless(a){
n = a.length;
// B: breaking condition
if (n==1) return a[0];
// L1: first loop
// let a1, a2 be arrays of size n/2
for (i=0; i <= (n/2)-1; i++) {
a1[i] = a[i];
a2[i] = a[n/2 +i];
}
// L2: second loop
for (i=0; i<=(n/2)-1; i++) {
for (j = i+1; j<=(n/2) -1; j++) {
if(a1[i] == a2[j])
a2[j] = 0;
}
}
// R: recursion
b1 = useless(a1);
b2 = useless(a2);
return max(b1, b2);
}
A(1) = 0
A(n) = ... n > 1
B executes 0 assignments
L1 executes 2*n/2 == n assignments
L2 executes at worst (if all elements are 0) n*(n-1)/2 assignments
thus A executes n(n+1)/2 assignments total before reaching R
A(1) = 0
A(n) = n(n+1)/2 + 2 * A(n/2) n > 1
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * A(n/4))
= n(n+1)/2 + 2 * ((n/2)((n/2)+1)/2 + 2 * ((n/4)((n/4)+1)/2 + 2 * A(n/8)))
= ...
= n(n+1)/2 + sum i from 1 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )
= sum i from 0 to x of ( 2^i * (n/(2^i))((n/(2^i))+1)/2 )