显然,您需要针对 Ranges.txt 中的每一行运行 Numbers.txt 中的每一行。
您可以只遍历 Numbers.txt,并且对于每一行,遍历 Ranges.txt。但这将永远花费数百万次读取整个 Ranges.txt 文件。
您可以将它们都读入内存,但这会占用大量存储空间,这意味着在您完成读取和预处理这两个文件之前,您将无法进行任何处理。
因此,您要做的是将 Ranges.txt 读入内存一次,然后将其存储为例如整数对的列表,但懒惰地读取 Numbers.txt,遍历每个数字的列表。
这种事情时时出现。通常,您希望将较大的集合放入外部循环,并使其尽可能惰性,而较小的集合进入内部循环,并对其进行预处理以使其尽可能快。但是,如果可以更有效地预处理更大的集合(并且您有足够的内存来存储它!),请反过来。
说到预处理,你可以做的比仅仅读入一个整数对列表要好得多。如果您对 Ranges.txt 进行排序,您可以通过二等分找到最接近的范围,然后只需检查(18 步),而不是详尽地检查每个范围(100000 步)。
这对 stdlib 来说有点麻烦,因为在使用 时很容易出错bisect
,但是有很多 ActiveState 配方可以使它更容易(包括从官方文档链接的一个),更不用说第三个了-party 模块之类的blist
或bintrees
在简单的 OO 界面中为您提供排序集合的模块。
所以,像这样的伪代码:
with open('ranges.txt') as f:
ranges = sorted([map(int, line.split()) for line in f])
range_values = {}
with open('numbers.txt') as f:
rows = (map(int, line.split()) for line in f)
for number, value in rows:
use the sorted ranges to find the appropriate range (if any)
range_values.setdefault(range, []).append(value)
with open('output.txt') as f:
for r, values in range_values.items():
mean = sum(values) / len(values)
f.write('{} {} {}\n'.format(r[0], r[1], mean))
顺便说一句,如果解析结果比仅split
在每一行上调用更复杂,我建议使用该csv
模块......但看起来这在这里不会成为问题。
如果不能将 Ranges.txt 放入内存,但可以放入 Numbers.txt,该怎么办?好吧,您可以对其进行排序,然后遍历 Ranges.txt,找到排序后的数字中的所有匹配项,然后写出该范围的结果。
这有点复杂,因为您必须 bisect_left 和 bisect_right 并迭代其间的所有内容。但这是唯一更难的方法。(在这里,第三方类将提供更多帮助。例如,使用 abintrees.FastRBTree
作为您的排序集合,它只是sorted_number_tree[low:high]
.)
如果范围可以重叠,你需要更聪明一点——你必须找到最接近的范围而不超过起点,以及最接近的范围而不超过终点,并检查两者之间的所有内容。但其中的主要技巧与上一版本使用的技巧完全相同。唯一的另一个技巧是保留两个范围的副本,一个按起始值排序,一个按结束排序,您需要将其中一个映射到另一个索引,而不仅仅是一个普通列表。