明らかに、Numbers.txt の各行を Ranges.txt の各行に対して実行する必要があります。
Numbers.txt を反復処理し、各行について Ranges.txt を反復処理することができます。しかし、これにはとてつもなく時間がかかり、Ranges.txt ファイル全体を何百万回も読み取ることになります。
両方をメモリに読み込むこともできますが、それには多くのストレージが必要であり、両方のファイルの読み込みと前処理が完了するまで処理を行うことができません。
つまり、Ranges.txt を一度メモリに読み込んで、代わりに int のペアのリストとして保存しますが、Numbers.txt を遅延して読み取り、各数値のリストを反復処理します。
このようなことはしょっちゅう出てきます。一般に、より大きなコレクションを外側のループに入れ、可能な限り遅延させたいと考えていますが、小さなコレクションは内側のループに入り、可能な限り高速にするために前処理されます。しかし、より大きなコレクションをより効率的に前処理できる場合 (そして、それを格納するのに十分なメモリがある場合) は、それを逆にします。
前処理について言えば、単に int のペアのリストを読み取るよりもはるかに優れた処理を行うことができます。Ranges.txt を並べ替えた場合、各範囲を徹底的にチェックする (100000 ステップ) のではなく、2 等分することなく最も近い範囲を見つけて、それをチェックする (18 ステップ) ことができます。
を使用するとオフバイワンエラーが発生しやすいため、これは stdlib では少し面倒ですが、簡単にするためbisect
の ActiveState レシピがたくさんあります (公式ドキュメントからリンクされたものを含む)。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 の間のすべてを繰り返す必要があるため、これはもう少し複雑です。しかし、それがより困難な唯一の方法です。(ここでは、サードパーティ クラスがさらに役立ちます。たとえば、bintrees.FastRBTree
並べ替えられたコレクションとして a を使用すると、それは単にsorted_number_tree[low:high]
.)
範囲がオーバーラップする可能性がある場合は、もう少し賢くする必要があります。開始点を超えずに最も近い範囲を見つけ、最後を超えずに最も近い範囲を見つけ、その間のすべてをチェックする必要があります。しかし、主なトリックは、最後のバージョンで使用されたものとまったく同じです。他の唯一のトリックは、範囲の 2 つのコピーを保持することです。1 つは開始値でソートされ、もう 1 つは終了値でソートされます。そのうちの 1 つは単なるリストではなく、もう 1 つのインデックスへのマップである必要があります。