数据结构与算法-Python实现(一)大O表示法

全球变暖可视化精选
上图为:Tableau可视化精选描述全球问题经典作品

一、前言

我们正处于一个数据大爆炸的时代,2019年全球每天收发2936亿封电子邮件、一辆联网汽车每天产生4TB的数据、全世界每天有50亿次在线搜索,其实当中都是数据的交互;每天打开科技专栏满满都是5G、大数据、人工智能、云计算,听过这么一句话:生活时代交叠地方的人,会感受到两个时代带来的冲击 和我们时代交叠的时代是什么?AI 现如今呢AI到底发展到什么程度了呢?我们社会上也出现了一种现象叫:人工智能泛化,人人都说人工智能,人人都说大数据,那到底什么是大数据?什么是人工智能?几个人能讲的清楚,举一个李开复博士曾经讲过的一个例子:一家做内衣的企业自称为人工智能企业。人工智能目前主要依赖于数据模型算法,基于历史数据对未来进行分类或者预测,理解数据结构与算法有助于我们去理解和实现机器学习对一些算法,并且在日常编写时我们可以用它来优化代码,总之数据结构与算法就像是我们的内功,所以打算写相关系列的文章用来记录和分享,一起回顾相关的知识点

二、算法分析

如何衡量算法的好坏?我们一般是从下面两种角度去考虑:

  • 算法消耗的计算机资源(空间复杂度):详细的来讲就是算法在执行过程中占用的存储空间或内存,但存储空间会受到问题本身的数据规模的变化影响,如果要区分哪些存储空间或内存是问题本身描述所需,哪些是算法占用,相对来说比较困难。
  • 算法的执行时间(时间复杂度):我们可以对程序进行实际运行测试,获得真实对运行时间

我们来用执行时间来衡量算法的性能看一个实例:

import time


def computing_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        over_time = time.time()
        print("Sum is %s required: %5f seconds" % (args[0], over_time - start_time))
    return inner


@computing_time
def sum_func(n):
    temp_num = 0
    for i in range(1, n+1):
        temp_num += i
    return temp_num


sum_func(100000)
sum_func(1000000)
sum_func(10000000)
sum_func(100000000)

运行结果如下:

Sum is 100000 required: 0.004882 seconds
Sum is 1000000 required: 0.054030 seconds
Sum is 10000000 required: 0.537019 seconds
Sum is 100000000 required: 5.220396 seconds

上面的代码是一个累加求和的函数,然后使用装饰器实现了一个程序计时功能,我们从10万开始每次运行都是上一次的10倍,我们从结果可以看到运行时间每次也是上一次的10倍。

上面我们使用的是一个迭代算法来实现一个数的累加,现在我们使用一个无迭代的方法来实现相同的函数:

import time


def computing_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        over_time = time.time()
        print("Sum is %s required: %5f seconds" % (args[0], over_time - start_time))
    return inner


@computing_time
def sum_func(n):
    return (n * (n + 1)) / 2


sum_func(100000)
sum_func(1000000)
sum_func(10000000)
sum_func(100000000)

运行结果如下:

Sum is 100000 required: 0.000001 seconds
Sum is 1000000 required: 0.000001 seconds
Sum is 10000000 required: 0.000000 seconds
Sum is 100000000 required: 0.000001 seconds

我们可以看到使用无迭代的方法后,随着参数增大我们代码的运行时间几乎不变;那么利用运行时间来衡量一个算法真的合适吗?答案是:不合适,因为运行时间会受到程序运行环境等多方面的影响,我们需要一种衡量指标它不会受到机器环境、运行时段、编程语言的影响,运行时间无法做到这一点。

因此,另一种更为通用的方法就出来了:「 大O符号表示法 」,即 T(n) = O(f(n))

三、大O表示法

衡量指标不能受到机器环境等影响那么就需要一个独立于具体程序或者机器的指标,那么就需要一个通用的基本操作来作为运行步骤的计量单位,每一种编程语言都有赋值语句,和具体都实现没有关系,所以赋值语句是一个合适选择。那么如果按照赋值语句来衡量那么一个算法在运行当中赋值语句多则运行时间就相对较长,赋值语句少则运行时间短,是这样吗?
在这里插入图片描述
我们可以看到当 n 被传入函数的时候,temp_num 是第一条赋值语句,然后进入循环后赋值语句的数据恰好等于 n 所以赋值语句的数量等于 T(n) = n + 1 那么 n 的大小就决定了赋值语句的次数,就决定了程序运行的时间,所以 n 被称为:问题规模 我们分析算法的目标就是要找出 问题规模 是如何影响一个算法的 运行时间 它们之前存在着怎样的函数关系。我们现在来细品 T(n) = n + 1 这个函数,我们看到 n 是T(n)函数中起决定性的因素,从动态的眼光看,无论函数多复杂,当问题达到某个数量级的时候,T(n)中的一部分会盖过其它部分的贡献,我们把函数中这一部分称为:数量级函数,它描述了T(n)中随着n增加而增加速度最快的主导部分,这种数量级函数,称为:大O表示法,记作 O(f(n)) 其中 f(n) 表示 T(n) 中的主导部分,什么是主导部分相信大家心里都有数了吧!我们一起来看下面的栗子:

  • T(n) = 1 + n (刚才那个累加函数)
    当 n 增大时,常数1在对结果的贡献(影响)可以忽略,所以可以去掉1,保留n作为主要部分,那么运行时间数量级就是 O(n) n 增大运行时间也线性增大
    n+1

  • T(n) = 5n^2 + 20n + 1000
    当 n 很小时,常数1000 对结果对贡献最大,但当 n 增大时 n^2 就会越来越重要,其它两项对结果影响就越来越小,同学系数 5 对于 n^2 的增长速度也不大,也可以忽略,所以可以在数量级中去掉 20n+1000 以及系数部分,大O表示法结果为:O(n^2)

  • 从代码来分析算法复杂度

a = 10
b = 12
c = 15
for i in range(n):
    for j in range(n):
        x = i * i
        y = j * j
        z = i * j
for k in range(n):
    r = a * k + 45
    v = b * b
d = 50

那么 T(n) = 3 + 3n^2 +2n +1 (赋值语句) 合并同类项后:T(n) = 3n^2 + 2n + 4 按照上述的方法分析,只保留高阶项n^2,去掉系数,表示为: O(n^2)

ok!大O表示法就介绍完了,Bye~

猜你喜欢

转载自blog.csdn.net/qq_42768234/article/details/104847462
今日推荐