您的位置:首页 > 其它

算法时间复杂度分析(1)

2017-05-01 21:26 2066 查看
如果有错误的地方,欢迎大家指正,只希望不要误导别人。

开篇:

     学习算法时间复杂度分析,首先要对O、o、Ω、ω、Θ这几个符号有基本的了解,下面将给出这几个符号详细的定义。

    1、大O符号:

        定义:

                设f和g是定义域为自然数集N上的函数,若存在正数c和n0,,使得对一切 n >= n0有 0 <= f(n) <= cg(n)成立, 则

                称f(n)的渐进上界是g(n),记做:f(n) = O(g(n))

        例子:

               f(n) = n^2 + n, g(n) = n^2; 当 c = 2, 只要n0 = 1, 就满足, f(n) <= cg(n)    ==>    这个时候称g(n)为f(n)的渐进上界,

               记作:f(n) = O(n^2)

        注意:

                1、f(n) = O(g(n)),f(n)的阶不高于g(n)的阶。

                2、可能存在多个正数c,只要指出一个即可。

                3、对前面有限个值可以不满足不等式。

                4、常函数可以写作O(1).

    2、小o符号:

         定义:

                设f和g是定义域为自然数集N上的函数,若对于任意正数c都存在n0,使得对一切 n >= n0,有 0 <= f(n) < cg(n)成立,

                则记作f(n) = o(g(n))

         例子:

               f(n) = n^2 + n , 则f(n) = o(n^3) c >= 1成立,因为n^2 + n < cn^3(n0 = 2)

               任给1 > c > 0, 取n0 > ⌈2/c⌉即可,因为

               cn >= cn0 > 2 (当 n >= n0)

               n^2 + n < 2n^2 < cn^3

         注意:

                1、f(n) = o(g(n)),f(n)的阶低于g(n)的阶。

                2、对不同正数c,n0不一样,c越小n0越大。

                3、对于前面有限个n值可以不满足不等式。

    3、大Ω符号:

         定义:

                设f和g是定义域为自然数集N上的函数,若存在正数c和n0,使得 对一切 n >= n0, 0 <= cg(n) <= f(n),成立,

                则称f(n)的渐进下界是g(n),记作f(n) = Ω(g(n));

         例子:

              f(n) = n^2 + n, g(n) = n^2

              当c = 1时,只要n0 = 1 就满足,cg(n) <= f(n)    ==>  
4000
 这个时候称g(n)为f(n)的渐进下界,记作:f(n) = Ω(n^2)

         注意:

                1、f(n) = Ω(g(n)),f(n)的阶不低于g(n)的阶。

                2、可能存在多个正数c,指出一个即可。

                3、对前面有限个n值可以不满足上述不等式。

    4、小ω符号:

         定义:

                设f和g是定义域为自然数集N上的函数,若对于任意正数c都存在n0,使得对于一切n >= n0,

                有 0 <= cg(n) < f(n) 成立,则记作f(n) = ω(g(n)).

         例子:

             设f(n) = n^2 + n,则 f(n) = ω(n),不能写f(n) = ω(n^2),因为c =2,不存在n0使得一切n > n0有下式成立

             cn^2 = 2n^2 < n^2 + n

         注意:

                1、f(n) = ω(g(n)),f(n)的阶高于g(n)的阶。

                2、对不同的正数c,n0不等,c越大n0越大。

                3、对前面有限个n值可以不满足不等式。

    5、Θ符号:

         定义:

                若f(n) = O(g(n)) 且 f(n) = Ω(g(n)),则记作:f(n) = Ω(g(n))

         例子:

                f(n) = n^2 + n. g(n) = 100n^2,那么有f(n) = Θ(g(n))

         注意:

                1、f(n)的阶与g(n)的阶相等

                2、对前面有限个n值可以不满足条件

渐进:

       经过上面的介绍,想必大家对这几个符号的定义有了基本的了解。那么,现在我们再来了解一下几个常用的定理。

    1、定理1:

                (1)如果n -> ∞,f(n) / g(n) 存在,并且等于某个常数 c > 0,那么, f(n) = Θ(g(n))

                (2) 如果n -> ∞,f(n) / g(n) = 0, 那么, f(n) = o(g(n))

                (3) 如果n -> ∞,f(n) / g(n) = +∞,那么, f(n) = ω(g(n))

     2、定理2:

                设函数f, g, h的定义域为自然数集合

                (1) 如果 f = O(g) 且 g = O(h), 那么 f = O(h)

                (2) 如果 f = Ω(g) 且 g = Ω(h),那么 f = Ω(h)

                (3) 如果 f = Θ(g) 和 g = Θ(h), 那么 f = Θ(h)

    3、定理3:

                假设函数 f 和 g 的定义域为自然数集,若对某个其它函数h,有f = O(h) 和 g = O(h), 那么 f + g = O(h)

        ps:该性质可以推广到有限个函数。算法由有限步骤构成。若每一步的时间复杂度函数的上界都是h(n),那么该算法的时间

                复杂度可以写作O(h(n))

入门:

            上面介绍了算法领域常见的几个渐进符号。这里重点再介绍一下大O符号。大O符号常被用在表示算法时间复杂度。 这里

     需要理清一些概念,首先,大O表示的是某个函数的渐进上界,在上面的定义中可以知道, 0 <= f(n) <= cg(n),这里g(n)的阶

     是可以高于f(n)的,所以,O(g(n))并不明确用来表示,最坏时间复杂度或平均时间复杂度,所以,通常你能看见的说法都是,

     最坏时间复杂度O(g(n)),和平均复杂度O(g(n))。那么,有没有专门用来表示最坏时间复杂度的符号和表示平均时间复杂度的

     符号?有,通常用W(g(n))表示最坏的时间复杂度,A(g(n))表示平均的时间复杂度。

             (1) 问题1:如何衡量算法的时间复杂度?

                          一个算法的执行时间,去掉不定因素(比如:计算机的运算能力等),算法的执行时间其实是受算法的输入和

                    算法本身的设计好坏所影响的。所以,我们衡量一个算法的执行时间,可以通过基本运算的执行次数来衡量。通常

                    用T(n)表示基本运算的执行次数。而什么叫基本运算?加、减、乘、除、比较等都是基本的运算。比如:一个基于

                    比较的排序算法,那么这个算法的基本运算就是比较,我们只要统计出这个基本运算的执行次数就能估算出算法的

                    时间复杂度。

             (2) 问题2: 渐进时间复杂度?

                           通过上一个问题的介绍,我们可以知道,一个算法的执行效率,其实是可以通过基本语句的执行次数来衡量。但

                     是,这样的方式估算算法复杂度太过麻烦,所以引入了渐进时间复杂度。渐进时间复杂度的好处在于,可以忽略低阶

                     项和最高阶项系数,这样估算时间复杂度会快速许多。

             (3) 问题3: 常数时间?

                          通常常数时间用O(1)表示,那么,什么情况下可以认为是常数时间?主要有两个情况。

                              1、当该执行语句与输入n无关时,可以认为该语句的执行时间为常数时间。

                              2、当该执行语句在 t 时间内能够执行完,也可以认为是常数时间。

简单例子:

             现在举几个简单的时间复杂度分析的例子。首先看下面一段代码。

        for (int i = 0; i < n; i++) {
            for (int j = i; j < n; j++) {
                count++;
            }
        }

             首先:n代表的是输入规模,其中基本操作语句是自加,那么count++执行了多少次?我们假设输入规模 n = 10,那么count

       的执行次数是 (10 + 1)*10/2 = 55。所以,T(n) = (n + 1)*10/2 = 1/2*n^2 + 1/2*n,当f,g在定义域为自然数N上的函数,当存在一

       个整数c和n0,使得  n >= n0时,0 <= f(n) <= cg(n),记作:f(n) = O(g(n)),g(n)称为f(n)的渐进上界,这里的T(n)就是f(n),当c = 2,

       n0 = 1, f(n) = 1/2*n^2 + 1/2*n <= cg(n) = 2*n^2 ==> 所以:f(n) = O(n^2)。

 

            对于上面的例子,最坏和平均复杂度都是O(n^2),因为该算法只与输入规模n有关。

            其实,多重循环中,如果每层循环都与输入规模有关的话,那有几层循环,时间复杂度就是n的几次方。

        现在再来看看另外一个例子:

public int binarySearch(int[] arr, int l, int r, int target) {

if(l > r)
return -1;

int mid = (l + r) / 2;

if(arr[mid] == target)
return mid;

if(target < arr[mid])
return binarySearch(arr, l, mid -1, target);
else
return binarySearch(arr, mid + 1, r, target);
}

              上面是一个简单的二分搜索。搜索过程,其实就是比较的过程,所以上述算法的基本运算就是比较。             

             

              现在我们试着分析一下二分搜索的平均时间复杂度。

              


              从上面的图片中,很容易可以看出,对 t = 1, 2, ..., k-1, 比较t次:2^(t-1)个。而比较k次的输入有2^(k-1) + n +1个

         这里k代表最多的比较次数,输入规模为n,则存在n+1种target输入是在序列中不存在的,也就是说要比较k次,所以,

         比较k次的输入是2^(k-1) + n + 1。

               我们假设 输入规模n = 2^k -1(n为奇数),并且,各种输入概率相等,则一共有1/(2n+1)种输入,那么

             


             通过上述计算,可以求出平均时间复杂度A(n) = logn+1/2,用渐进符号表示为:O(logn)

       时间复杂度的分析,尤其是递推时间复杂度分析比较麻烦,涉及的方法有迭代法、差消法、递归树法、主定理法等。

       更详细的算法时间复杂度分析可以先参考:http://www.cnblogs.com/python27/archive/2011/12/09/2282486.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: