我有Leetcode问题的这三种解决方案,在这里并没有真正理解时间复杂度的差异。为什么最后一个函数的速度是第一个函数的两倍?
68毫秒def numJewelsInStones(J, S):
count=0
for s in S:
if s in J:
count += 1
return count
40毫秒
def numJewelsInStones(J, S):
return sum(s in J for s in S)
32毫秒
def numJewelsInStones(J, S):
return len([x for x in S if x in J])
答案 0 :(得分:4)
为什么最后一个函数的速度是第一个函数的两倍?
以大O表示法表示的分析时间复杂度对于所有人来说都是相同的,但是服从常数。那是例如O(n)
实际上是O(c*n)
的意思,但是在比较时间复杂度时,约定会忽略c
。
每个函数都有一个不同的c
。特别是
sum
可能是用C代码执行的(求和部分,加上数字)len
是对数组的简单属性“单一操作”查找,可以在恒定时间内完成,而sum
需要n
的添加操作。因此c(for) > c(sum) > c(len)
,其中c(f)
是功能/声明f
的假设固定开销度量。
您可以通过disassembling每个函数来检查我的假设。
除此之外,由于系统中正在运行其他进程,因此测量值可能会受到变化的影响。为了从您的分析中消除这些影响,请取每个函数至少1000次调用的平均执行时间(您可能会发现c
小于此变化,尽管我并不希望如此)。
这些功能的时间复杂度是多少?
请注意,尽管所有函数都具有相同的大O时间复杂度,但是根据您用于J, S
的数据类型,后者会有所不同。如果J, S
的类型为:
dict
,函数的复杂度将在O(n)
set
,函数的复杂度将在O(n)
list
,函数的复杂度将在O(n*m)
中,其中n,m
分别是J, S
变量的大小。请注意,如果n ~ m
会有效地变成O(n^2)
。换句话说,请勿使用list
。 为什么数据类型很重要?因为Python的in
运算符实际上只是为特定类型实现的成员资格测试的代理。具体来说,dict
和set
成员资格测试在恒定时间的O(1)
中工作,而list
的成员资格测试在O(n)
时间中工作。因为在list
情况下J
的每个成员都有S
的每个成员通过,反之亦然,所以总时间在O(n*m)
中。有关详情,请参见Python's TimeComplexity wiki。
答案 1 :(得分:3)
大的O符号表示时间复杂度,它说明解决方案如何随着输入集的增长而增长。换句话说,它们如何相对相关。如果您的解决方案是O(n),则随着输入的增长,完成时间线性增长。更具体地说,如果解决方案为O(n),并且当数据集为100时需要10秒,那么当数据集为1000时应该大约需要100秒。
您的第一个解决方案是O(n),我们知道这是因为S中的for循环,因为for循环了整个数据集一次。如果J中的s假设J是一个集合或字典可能是恒定时间O(1),则其背后的原因有点超出了问题的范围。结果,总的来说第一个解是线性时间O(n)。
如果您对多个数据集进行测试并随时间进行平均,则其他解决方案之间的细微差别很可能可以忽略不计,其中要考虑启动时间和影响测试结果的其他因素。此外,Big O表示法会丢弃系数,因此,例如O(3n)〜= O(n)。
您会在所有其他解决方案中注意到您具有相同的概念,遍历整个集合并检查集合或字典中是否存在。结果,所有这些解都是O(n)。时间的差异可以归因于同时运行的其他进程,所使用的某些内置函数是纯C事实,也归因于测试不足所致的差异。
答案 2 :(得分:0)
第二个函数比第一个更快,因为使用了generator而不是loop。第三个函数比第二个函数快,因为第二个求和生成器输出(返回类似于list的东西),但是第三个函数-仅计算其长度。