python中求和时间复杂度最低的_求和算法的时间复杂度

本文分析了一个Python求和函数的时间复杂度,指出原始实现为O(n²),并解释了原因。通过修改代码,去除切片操作,实现了O(n)的时间复杂度。同时强调了数据结构选择和操作对算法效率的影响,以及如何通过调整代码来优化性能。
摘要由CSDN通过智能技术生成

编辑:根据关于[::]的O(n)性能的评论进行的更正。在

TL;DR:可能是O(n),,但您的版本是O(n²)。

记住,所有的大O符号都假设“乘以一个常数”。也就是说,O(n)实际上意味着O(k*n),而O(logn)实际上意味着O(k*logn)。在

让我们看看你的第一个例子:def sum(numberSequence):

assert (len(numberSequence) > 0)

if (len(numberSequence) == 1):

return numberSequence[0]

else:

return sum(numberSequence[-1]) + numberSequence[:-1]

第一行是assert加compare加len。len操作对于列表和元组来说是一个固定的时间(但是对于其他数据结构可能不是这样!小心!),compare是一个常量时间,assert实际上是一个常量时间,因为如果它失败了,整个过程就会崩溃,我们停止计算。所以让我们只调用assert一个函数调用加上一个比较加上一个返回。在

现在,这个函数被调用了多少次?很明显,终止条件表示一次,每隔一次它在一个比前一个列表短一个的列表上递归。因此函数将被调用len(numberSequence)次,在我们的目的中是n。在

所以我们有

^{pr2}$

接下来,我们有标记递归终止条件的if语句。显然,这个语句只会成功一次(这是终止条件,对吗?只在末尾发生…)所以这是每次比较,每和一次,它是一个常数索引的返回。在n * compare

+ 1 * constant index

+ 1 * return

最后,还有else:分支。我很确定你有个bug,应该是这个(注意冒号的位置):return sum(numberSequence[:-1]) + numberSequence[-1]

在这种情况下,返回一个常量负索引查找和一个切片的递归函数调用的和。只有在递归结束时才这样做,所以n-1次。在(n - 1) * constant negative index lookup

+ (n - 1) * slice

+ (n - 1) * recursive call

+ (n - 1) * return

但是等等!如果您四处寻找询问如何制作列表副本的人,您会发现一个常见的Python习惯用法是copy = orig[:]。这是因为切片操作会复制它正在切片的列表的子范围。所以当你说numberSequence[:-1]你真正说的是{}。在

这意味着slice操作是O(n),,但从好的方面来说,它是用C写的,所以常数要小得多。在

让我们把这些加起来:1 * call

+ n * assert

+ n * len

+ n * compare

+ n * compare

+ 1 * constant index

+ 1 * return

+ (n - 1) * constant negative index lookup

+ (n - 1) * (c * n) slice

+ (n - 1) * recursive call

+ (n - 1) * return

如果我们假设常数指数和常数负指数花费的时间相同,我们可以合并它们。显然我们可以合并返回和调用。这给我们留下了:n * call

+ n * assert

+ n * len

+ n * compare

+ n * compare

+ n * constant (maybe negative) index

+ n * return

+ (n - 1) * (c * n) slice

根据“规则”,这是O(n²)。这意味着所有的行为细节都被抛在了一边,有利于那个大胖子O(n²)。

但是:

如果len操作不是O(1)—也就是说,时间不变—那么函数很可能会因此变成O(n²)。在

如果index操作不是O(1),由于底层实现细节的原因,函数可能会变成O(n²)或O(n logn)。在

因此,您已经实现了一个算法,它可以是O(n),它使用的Python操作符本身就是O(n)。您的实现是“固有的”O(n²)。但它是可以修复的。即使修复了,超出您控制范围的东西也会使您的代码变慢。(但是,这超出了你的控制范围,所以。。。忽略它!)在

我们如何修正你的代码使之成为O(n)?把那一片去掉!反正你也不需要,对吧?你只需要追踪射程。在def sum(numberSequence, start=0, end=None):

assert (len(numberSequence) > 0)

if end is None:

end = len(numberSequence) - 1

if end == start:

return numberSequence[start]

else:

return sum(numberSequence, start, end-1) + numberSequence[end]

在这段代码中,我做的和你做的差不多,两个

差异。首先,我添加了一个特殊情况来处理最终用户调用的情况,只使用序列作为参数。第二,当然,没有切片。用那个o顺便说一句,代码不再是天生的O(n²)。

你可以对你的另一个例子做同样的数学运算,做同样的改变,但是它更复杂。但是,我要提醒你,I=0..n-1的2I之和是2n-1。正如@lollercaster指出的,天下没有免费的午餐:你必须把所有的数字加起来。在

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值