zoukankan      html  css  js  c++  java
  • 01 算法介绍以及时间复杂度

    一. 什么是算法分析?

      - 案例引入:刚接触编程的学生经常会将自己编写的程序和别人的程序做比对,获取在比对的过程中会发现双方编写的程序很相似但又各不相同。那么就会出现一个有趣的现象:两组程序都是用来解决同一个问题的,但是两组程序看起来又各不相同,那么哪一组程序更好呢?例如下述代码:

    复制代码
    def sumOfN(n):
        theSum = 0
        for i in range(1,n+1):
            theSum = theSum + i
    
        return theSum
    
    print(sumOfN(10))
    复制代码
    复制代码
    def foo(tom):
        fred = 0
        for bill in range(1,tom+1):
            barney = bill
            fred = fred + barney
    
        return fred
    
    print(foo(10))
    复制代码

      分析:

        - 哪个函数更好,答案取决于你的标准。如果你关注可读性,函数 sumOfN 肯定比 foo 好。事实上,你可能已经在介绍编程的课程中看到过很多例子,他们的目标之一就是帮助你编写易于阅读和理解的程序。然而,在本课程中,我们对算法本身的表示更感兴趣(当然我们希望你继续努力编写可读的,易于理解的代码)。

        - 算法分析是基于每种算法使用的计算资源量来比较算法。我们比较两个算法,说一个比另一个算法好的原因在于它在使用资源方面更有效率,或者仅仅使用的资源更少。从这个角度来看,上面两个函数看起来很相似。它们都使用基本相同的算法来解决求和问题。

      - 问题:a+b+c = 1000  a**2 + b**2 = c**2 (a,b,c均为自然数),求出a,b,c可能的组合?

    for a in range(0,1001):
        for b in range(0,1001):
            for c in range(0,1001):
                if a**2+b**2 == c**2 and a+b+c==1000:
                    print(a,b,c)
    for a in range(0,1001):
        for b in range(0,1001):
            c = 1000 - a - b
            if a**2+b**2 == c**2 and a+b+c==1000:
                    print(a,b,c)

      分析:很明显上述两中问题的解决方案是不同的。那如何判定上述两种解决方案(算法)的优劣呢?有同学会说,观察计算两种算法使用耗费计算机资源的大小和它们的执行效率呀!但是我想说的是,一些较为复杂的算法,它耗费计算机资源的大小和执行效率我们很难能够直观的从算法的编码上分析出来。所以我们必须采用某种量化的方式求出不同算法的资源耗费和执行效率的具体值来判定算法之间的优劣。问题来了,如何进行量化计算呢?方法有两种:

      - 方法1:计算算法执行的耗时(不推荐)。

    复制代码
    import time
    start_time = time.time()
    for a in range(0,1001):
        for b in range(0,1001):
            for c in range(0,1001):
                if a**2+b**2 == c**2 and a+b+c==1000:
                    print(a,b,c)
    end_time = time.time()
    print(end_time-start_time)
    
    #执行结果为:
    0 500 500
    200 375 425
    375 200 425
    500 0 500
    1221.7778379917145
    复制代码
    复制代码
    import time
    start_time = time.time()
    for a in range(0,1001):
        for b in range(0,1001):
            c = 1000 - a - b
            if a**2+b**2 == c**2 and a+b+c==1000:
                    print(a,b,c)
    end_time = time.time()
    print(end_time-start_time)
    
    #执行结果为:
    0 500 500
    200 375 425
    375 200 425
    500 0 500
    1.410386085510254
    复制代码

      注意:单靠执行时间可以反应算法的效率吗?不能。跟机器(执行环境)存在很大关系。

      方法2:计算算法的时间复杂度(推荐)

        - 时间复杂度:量化算法需要的操作或者执行步骤的数量。

    四.时间复杂度

      - 我们试图通过算法的执行时间来判定算法的优劣。但是仅仅根据执行时间判定算法优劣有些片面,因为算法是独立于计算机的。重要的是量化算法需要的操作或者步骤的数量。选择适当的基本计算单位是个复杂的问题,并且将取决于如何实现算法。对于先前的求和算法,一个比较好的基本计算单位是对执行语句进行计数。

      - 在 sumOfN 中,赋值语句的计数为 1(theSum = 0) 加上 n 的值(我们执行 theSum=theSum+i 的次数)。我们通过函数 T 表示 T(n)=1+n。参数 n 通常称为“问题的规模”,我们称作 “T(n) 是解决问题大小为 n 所花费的时间,即 1+n 步长”。在上面的求和函数中,使用 n 来表示问题大小是有意义的。我们可以说,100,000 个整数和比 1000 个问题规模大。因此,所需时间也更长。我们的目标是表示出算法的执行时间是如何相对问题规模大小而改变的。

        - 计算机科学家更喜欢将这种分析技术进一步扩展。事实证明,操作步骤数量不如确定 T(n) 最主要的部分来的重要。换句话说,当问题规模变大时,T(n) 函数某些部分的分量会超过其他部分。函数的数量级表示了随着 n 的值增加而增加最快的那些部分。

        - 数量级通常称为大O符号,写为 O(f(n))。它表示对计算中的实际步数的近似。函数 f(n) 提供了 T(n) 最主要部分的表示方法。T(n)的最主要部分是:在上述示例中,T(n)=1+n。当 n 变大时,常数 1 对于最终结果变得越来越不重要。如果我们找的是 T(n) 的近似值,我们可以删除 1。因此T(n)中最主要的部分为n,因此f(n)=n。因此在sumOfN函数对应的算法的时间复杂度可即为O(f(n)),即为O(n)。

          - 另外一个示例,假设对于一些算法,确定的步数是 T(n)=5n^2+27n+1005。当 n 很小时, 例如 1 或 2 ,常数 1005 似乎是函数的主要部分。然而,随着 n 变大,n^2 这项变得越来越重要。事实上,当 n 真的很大时,其他两项在它们确定最终结果中所起的作用变得不重要。当 n 变大时,为了近似 T(n),我们可以忽略其他项,只关注 5n^2 。系数 5 也变得不重要。我们说,T(n) 具有的数量级为 f(n)=n^2,或者 O( n^2 )

      - 常见的时间复杂度:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)  < O(n!) < O(n^n)
      - 案例分析:计算下列算法的时间复杂度
    复制代码
     1 a=5
     2 b=6
     3 c=10
     4 for i in range(n):
     5    for j in range(n):
     6       x = i * i
     7       y = j * j
     8       z = i * j
     9 for k in range(n):
    10    w = a*k + 45
    11    v = b*b
    12 d = 33
    复制代码

      分析:前三行赋值语句执行操作步骤为3,5-8行的执行操作步骤为3n,结合第四行的外部循环,整个内外循环的执行步骤为3n^2。9-11行为2n,12行为1。最终得出T(n)=3+3n^2+2n+1,简化操作后T(n)=3n^2+2n+4。通过查看指数,我们可以看到 n^2 项是最重要的,因此这个代码段是 O(n^ 2)。当 n 增大时,所有其他项以及主项上的系数都可以忽略。

    四.数据结构:

      - 对于数据(基本类型的数据(int,float,char))的组织方式就被称作为数据结构。数据结构解决的就是一组数据如何进行保存,保存形式是怎样的。

      - 案例:需要存储一些学生的学生信息(name,score),那么这些数据应该如何组织呢?查询某一个具体学生的时间复杂度是什么呢?

        - 组织形式1:  

    [{'name':'zhangsan','score':100},
     {'name':'lisi','score':99}
    ] 

        - 组织形式2:

    [('zhangsan',100),
     ('lisi',99)
    ] 

        - 组织形式3:

    {'zhangsan':{'score':100},
     'lisi':{'score':99}
    } 

      - 三种组织形式基于查询的时间复杂度分别为:O(n),O(n),O(1)

      - 发现:python中的字典,列表,元组本身就是已经被封装好的一种数据结构啦。使用不同的数据结构进行数据的存储,所导致的时间复杂度是不一样。因此认为算法是为了解决实际问题而设计的,数据结构是算法需要处理问题的载体。

  • 相关阅读:
    数据库>SQL Server2005>第4季SQL从入门到提高>13练习1
    重学>ASP.NET小实例>DataReader手工分页
    数据库>SQL Server2005>第4季SQL从入门到提高>8Uoin
    世界上最快乐的人>空性的禅修练习
    数据库>SQL Server2005>第4季SQL从入门到提高>11练习和12练习1
    数据库>SQL Server2005>随机遇到问题>查询name重复,id不重复,时间最新的记录
    XSS CSRF 攻击
    js随笔小技巧
    javascript 常用开源函数库以及学习网站
    php 检测用户是否关闭浏览器
  • 原文地址:https://www.cnblogs.com/a2534786642/p/10998986.html
Copyright © 2011-2022 走看看