【发布时间】:2020-04-12 12:16:56
【问题描述】:
我正在尝试优化在竞争性编程网站上超时的解决方案。我开始使用 cProfile,它似乎显示 bisect_right() 需要 4 倍于 insort_right() 的时间。这是在输入列表上运行的分析,包含超过 40k 个条目:
89936 function calls in 3.596 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 3.596 3.596 <string>:1(<module>)
1 0.180 0.180 3.596 3.596 leetcode.py:38(go)
1 3.357 3.357 3.415 3.415 leetcode.py:6(numSubarrayProductLessThanK)
44965 0.045 0.000 0.045 0.000 {built-in method _bisect.bisect_right}
44965 0.014 0.000 0.014 0.000 {built-in method _bisect.insort_right}
1 0.000 0.000 3.596 3.596 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.print}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
我认为所有列表插入都是 O(n),因为平均必须移动 n/2 个元素。并且简单地确定插入排序列表的位置将是 O(log n)。但在配置文件报告中,它看起来颠倒了:插入器insort_right() 比位置确定器bisect_right() 慢。我哪里错了?代码如下:
from bisect import insort, bisect
class Solution:
def numSubarrayProductLessThanK(self, nums, k):
if k == 0:
return 0
result = 0
product = 1
products = [1]
products_n = 1
for num in nums:
product *= num
factor_min = product // k
n_factors_less = products_n - bisect(products, factor_min)
result += n_factors_less
insort(products, product)
products_n += 1
return result
感谢您的关注。
【问题讨论】:
-
stackoverflow.com/questions/1110332/performance-of-list-insert 非常密切相关(其他人也对 bisect 性能感到好奇),但不是重复的。
-
...也就是说,就您的目标是弄清楚实际性能而言,我可以看到我们进入了与您的硬件细节相关的领域——big-O 描述了如何当然,操作的数量会随着数据结构的大小而变化,但它并没有说明这些操作实际花费在挂钟时间中多长时间的常数因素。
-
您发布的代码调用
bisect和insort,参数完全不同。看起来insort调用可能更适合分支预测,和/或它们可能具有更好的缓存行为。 (看起来insort总是用product调用,最终会出现在products列表的末尾。)