【发布时间】:2019-12-06 10:18:28
【问题描述】:
有一个未排序的列表a 和一个范围列表,如ranges = [(10, 20), (30, 50), (15, 35) ...]。 a 中的最大值为uint64_t。目标是计算每个范围的元素数量。正常的解决方案非常直观,只需计算范围内的元素并打印结果即可。但问题来自在线法官。我厌倦了几个解决方案,但对于每个解决方案,OJ 都给出了超出时间限制。
a 的最大长度为 10,000,000,ranges 的最大长度为 1,000,000。
测试列表a 有1000 万个随机数和ranges 有100 万对范围:
import numpy as np
a = list(np.random.randint(low=1, high=0x7fffffffffffffff, size=10_000_000))
ranges = []
for _ in range(1_000_000):
x, y = np.random.randint(low=1, high=0x7fffffffffffffff, size=2)
ranges.append((x, y) if x < y else (y, x))
第一个解决方案是:
import bisect
a.sort()
low_d = {}
up_d = {}
def count(r):
low, up = r
if low not in low_d:
l = bisect.bisect_left(a, low)
low_d[low] = l
else:
l = low_d[low]
if up not in up_d:
u = bisect.bisect_right(a, up, lo=l)
up_d[up] = u
else:
u = up_d[up]
return u - l
result = [*map(count, ranges)]
缺点很明显,当a 很大时,sort() 非常耗时。
原来的第二种方案比上面的方案慢很多。
被遗弃了。
两种解决方案都导致 TLE 错误。我使用的OJ就像一个黑盒子,我不知道它用来测试程序的测试例子。
由于程序是在OJ上运行的,所以不允许使用numpy。
有什么方法可以优化性能吗?
【问题讨论】:
-
您也可以对范围进行排序而不是数组,例如开始。到最后也许还会再来一次。
-
@keiv.fly 受你启发,将开始列表和结束列表合并,然后对合并后的列表进行排序。对于
a上只有一个循环,dictd用于保存d[x] = (the number of elements before x, the number of elements including x)。但是,速度并没有太大提升(bisect:~20,这个方法:~19)。因为主要的耗时部分是a.sort()。您对此有进一步的想法吗?
标签: python list performance optimization