前缀平均值(Prefix Averages)的三种算法实现分析

概念介绍

对于Python中给定的序列(list),同时存在一个前缀平均值序列。由n个数字组成的序列S,计算一个序列A,A[j]是S[0], S[1],……,S[j]的平均值,j = 0,……,n - 1。即:$ A[j] = \frac{\sum_{i = 0}^{j} S[i]}{j + 1} $。

实现一

来看第一种实现方式:

def prefix_average1(S):
    """Return list such that, for all j, A[j] equals average of S[0], ..., S[j]."""
    n = len(S)
    A = [0] * n                   # create new list of n zeros
    for j in range(n):
        total = 0                 # begin computing S[0] + ... + S[j]
        for i in range(j + 1):
            total += S[i]
        A[j] = total / (j + 1)    # record the average
    return A

分析:

  • n = len(S), 执行恒定的时间。
  • A = [0] * n,初始化序列长度为n,元素的所有值为0,因此,其时间复杂度为O(n)。
  • 接着是双层嵌套,外层循环由j控制,执行n次,j = 0,…,n - 1。total = 0和A[j] = total / (j + 1)分别执行n次。因此外层循环的时间复杂度为O(n)。
  • 再来看内层循环,total += S[i],执行次数由i和j共同确定。总共执行1+2+3+…+n = n(n + 1) / 2,因此,总得执行次数为O($ n^2 $)。

综上分析,整个算法的时间复杂度为O($ n^2 $)

实现二

来看第二种实现方式:

def prefix_average2(S):
    """Return list such that, for all j, A[j] equals average of S[0], ..., S[j]."""
    n = len(S)
    A = [0] * n                                # create new list of n zeros
    for j in range(n)::
        A[j] = sum(S[0: j + 1]) / (j + 1)      # record the average
    return A

与实现一不同的是,实现二用了一个更简单的计算S[0] + ... + S[j]的方式,即sum(S[0: j + 1])。使用这种方式虽然简化了算法的表达,但仍需要考虑其内部的算法时间复杂度。
观察sum(S[0: j + 1]),首先使用list的切片操作获取子序列,创建一个长度为j + 1的序列,则其时间复杂度为O(j + 1)。则对于循环来说,A[j] = sum([0: j + 1])的时间复杂度仍然为1 + 2 + … + n。
综上分析,则总得时间复杂度仍为O($ n ^ 2 $)

实现三

最后一种实现方式:

def prefix_average3(S):
    """Return list such that, for all j, A[j] equals average of S[0], ..., S[j]."""
    n = len(S)
    A = [0] * n                                # create new list of n zeros
    total = 0                                  # compute prefix sum as S[0] + S[1] + ...
    for j in range(n):
        total += S[j]                          # update prefix sum to include S[j]
        A[j] = total / (j + 1)                 # compute average based on current sum
    return A

在前两种算法中,对于j的每个值重新计算前缀和。对于每个j,则花费了O(j)的运算时间,总共加起来,则是O($ n ^ 2 $)的时间复杂度。
第三个算法的分析:

  • 初始化变量n和total,时间复杂度为O(1)。
  • 初始化序列A,时间复杂度为O(n)。
  • 对于单个for循环,其时间复杂度为O(n)。
  • 循环体执行n次,j = 0, ..., n -1。total += S[j]和A[j] = total / (j + 1)则分别执行n次。

综上分析,该算法的时间复杂度为O(n)

总结

由此可知,不同的算法实现,会有不同的算法时间复杂度。而随着n的增长,算法时间复杂度的差值会显著增长,这对于实际软件项目的优化也是挺可观的。

转载于:https://www.cnblogs.com/jeffrey-yang/p/9138379.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值