难以掌握Big Oh复杂性

时间:2016-02-04 15:45:47

标签: recursion big-o time-complexity

这不是家庭作业,而是课堂上练习的问题。我知道没有解决方案的关键。我想看看我做得对。另外,我如何找到时间复杂度?以下是我在此底部的解决方案。

确定以下程序片段的Big Oh复杂性

1)

void sum1(int n){
sum1 = 0;
for(i=1; i<=n; i++)
for (j=1; j<=n; j++)
sum1++;
}

2)

void sum2(int n){
sum2 = 0;
for(i=1; i<=n; i++)
for (j=1; j<=i; j++)
sum2++;
}

3)

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++){
    a1[i] = a[i];
    a2[i] = a[n/2 +i];
}
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;
        }
    }
    b1 = useless(a1);
    b2 = useless(a2);
    return max(b1,b2);
}

4)

void sum3(int n) {
sum = 0;
for(i=0; i<sqrt(n)/2; i++)
sum++;
for(j=0; j<sqrt(n)/4; j++)
sum++;
for(k=0; k<8+j; k++)
sum++;
}

5)

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++;
}
下面是我的解决方案,&#39;?&#39;意味着我不知道该放什么

1)

void sum1(int n){
sum1 = 0;             -->  1
for(i=1; i<=n; i++)   -->  n
for (j=1; j<=n; j++)  -->  n^(2)
sum1++;               -->  ?
}

答案:O(n ^(2))

2)

void sum2(int n){   
sum2 = 0;             --> 1
for(i=1; i<=n; i++)   --> n
for (j=1; j<=i; j++)  --> n^(2)
sum2++;               --> ?
}

答案:O(n ^(2))

3)

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);              --> ?
}

我非常坚持这个......

4)

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++;                      --> ?
}

答案:O(n ^(3))

5)

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++;                      --> ?
}

答案:O(n ^(3))

2 个答案:

答案 0 :(得分:1)

让我们考虑一下你已经解决的问题:

void sum2(int n){   
    sum2 = 0;
    for(i=1; i<=n; i++)
        for (j=1; j<=i; j++)
            sum2++;
}

我们计算在sum2上完成的增量数量:sum2++为1增量。因此,我们得到以下条款:

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)

让我们考虑3)

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);
}

我们正在计算aX的分配数量:a[j] = 0;是1个分配,a1[i] = a[i];也是。aX[I]。 (注意,我们可以很容易地计算数组访问次数A(n))。我们将此号码称为n,其中a是输入数组A的长度。我们注意到A(1) = 0 A(n) = ... n > 1 的两个目标案例:

R

在考虑递归之前,让我们看看我们对一般情况的成本(评论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

然后跟随两个输入大小的一半递归 - 所以对于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)。您可以求解确切的数字或近似DECLARE @Temp TABLE ( UserId nvarchar(128), DateCreated Date, DOfYear int ) INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-01-19'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-01-24'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-01-28'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-01-29'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-02-01'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-02-02'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-02-03'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-02-07'); INSERT INTO @Temp (UserId, DateCreated) values ('uid123', '2016-02-19'); WITH CTE AS ( SELECT DateCreated, StartDate = Dateadd(day,-ROW_NUMBER() OVER (ORDER BY DateCreated), DateCreated) FROM @Temp ) SELECT [LastDate] = MAX(DateCreated), TotalDays = COUNT(1) FROM CTE GROUP BY StartDate Order By StartDate desc ,它们都在相同的复杂性类别中。

答案 1 :(得分:1)

@BeyelerStudios到目前为止做得很好;我会尽量不重复。

认为你已经拥有的一些一般原则(大部分):

  • 对于普通的线性语句,您需要先验对语句复杂性的了解。基本算术运算和数组访问是O(1)。
  • 语句序列的顺序是序列中任何语句的最高顺序。
  • 决策分支的顺序是任何分支的最高顺序。
  • 循环的顺序是循环的重复次数乘以循环体的顺序。
  • 递归调用的顺序是递归的重复次数是函数体的顺序。

现在问题#4:

这不是O(n ^ 3):请注意,循环嵌套。每个都包含一个增量语句O(1)。前两个循环是O(sqrt(n));第三个使用 j 的最终值加上一个常量,因此它也是O(sqrt(n))。特别注意O(sqrt(n))&lt; O(n),你用过的。平方根是n ^(1/2)。

问题#5:

每个循环都是O(sqrt(n)),但它们现在是嵌套的。这给你sqrt(n)^ 3,或n ^(3/2)。

将来要注意的最后一个细节是sqrt(n)不一定是O(1);这取决于实施。现代芯片在板上具有sqrt功能,可在O(n)时间内处理所有本机整数和浮点数。但是,有一些任意大的浮点数和整数的数据类型,其实现是O(log(n))。如果您的老师没有对该操作做出总结性决定,请留意sqrt - 以及其他超越函数 - 潜入循环体。