我正在尝试遍历两个循环,第一个segment_arr
是一条线的小段(例如0.1m)的列表(按沿线的距离排序),第二个fail_sections
是该行较大部分(例如5m)的列表,这些部分以某种方式“失败”(也按沿行的距离排序)。最终,我尝试合并浮点范围-有一些答案here,但都基于整数,但有一些关于重叠的警告。
我有一个非常幼稚的版本,对于我的目的来说似乎性能足够,但是这让我开始思考,如何使它更有效:
new_seg_array = []
for seg in segment_arr:
segfail = False
for fail_sec in fail_sections:
if seg.start_dist >= fail_sec.start_dist and seg.end_dist <= fail_sec.end_dist:
segfail = True
seg_data = Segment(start_dist=seg.start_dist,end_dist=seg.end_dist, does_fail=segfail)
new_seg_array.append(seg_data)
主要问题是第二个循环浪费了迭代次数,因为第二个范围远远超出了我们在第一个范围内的位置,所以该条件不可能为真。我考虑过使用生成器表达式,例如
filtered_fail_sections = (x for x in fail_sections where x.start_distance > seg.start_distance and x.end_distance < seg.end_distance)
for fail_sec in filtered_fail_sections:
过滤相关的故障段,但令我惊讶的是,这只是在发生器中进行过滤工作。 Python中是否有任何办法逐渐减小第二个循环的范围,因此不再迭代因为不再在第一个循环的范围内而不再相关的元素?因此,随着时间的流逝,第二个循环将逐渐变小,直到没有任何循环为止,这大概将有助于更大数据集的性能。还是可能进行其他任何主要的效率改进?
答案 0 :(得分:1)
您可以将当前“活动”的故障段按其结束值存储在堆上,然后在该结束值小于当前段末尾时从堆中弹出它们。然后,如果堆上有任何元素,则当前段将失败。
import heapq
segments = [(1,2), (3,4), (5,7), (9,10)]
fail = [(0,4), (8,11)]
idx = 0
covering = []
for (start, end) in segments:
while idx < len(fail) and fail[idx][0] <= start:
heapq.heappush(covering, fail[idx][1])
idx += 1
while covering and covering[0] < end:
heapq.heappop(covering)
print(start, end, covering)
示例输出:
1 2 [4]
3 4 [4]
5 7 []
9 10 [11]
这样,您不仅可以知道当前段是否失败,而且还可以知道相交的故障段和故障段的数量。请注意,如果要将整个失败的段放到堆上,则应使用元组(end, f_seg)
以确保具有最低end
的元组在堆上排在首位。
当然,如果您不需要知道相交的故障段数或故障段数,则只需跟踪任何已启动的故障段的最大终止值即可,而无需使用堆。
idx = 0
last_fail = -1
for (start, end) in segments:
while idx < len(fail) and fail[idx][0] <= start:
last_fail = max(last_fail, fail[idx][1])
idx += 1
print(start, end, last_fail >= end)
对于 n 段和 m 故障段,基于堆的解决方案的complexity为 O(n + mlogm) O(n + m)不带堆。 (假设两个列表都已排序。)