追加の仮定
この回答は、次の追加の前提に基づいています。
- ログの先頭のタイムスタンプを簡単に決定できます。
- しゃっくりの位置を保存することが可能です(オプション)
検索アルゴリズム
検索は実際には2つの異なるアルゴリズムに分割されます。ログの先頭より後のタイムスタンプを持つログを検索する場合、ログがしゃっくりで見つからないことがわかっているので、以下の非しゃっくり検索を使用します。ログの開始前にタイムスタンプを検索する場合は、代わりにしゃっくり検索を使用します。タイムスタンプではなく他の基準で検索する場合、95%のカバレッジのため、最初に非しゃっくり検索を試し、見つからない場合はしゃっくり検索を試します。
必要に応じて、前処理ステップにより、一時的な検索を高速化できます。
前処理(オプション)
可能であれば、線形検索を使用してデータを事前に分析し、しゃっくりデータの位置を見つけます。これは、これらの範囲を保存できる可能性に完全に依存します。これは、ログの5%にすぎない場合に可能である可能性があります(そうでない場合、パフォーマンスが低下します)。
新しいログを書き込むときは常に、対応するデータ構造を更新する必要があります。少なくとも、前処理が実行されたログの時点までを通知できる必要があります。
非しゃっくり検索
非しゃっくりデータの検索は、バイナリ検索と線形検索の組み合わせによって可能です。通常のバイナリ検索を実行しますが、ピボット要素にログの開始前にタイムスタンプが付けられている場合、つまりピボット要素がしゃっくりである場合、ピボット要素の前の最初のログエントリを特定し、それを実際のピボット要素として使用する必要があります。あなたの二分探索の。
ログの開始後のタイムスタンプを持つこの最初のログエントリは、hiccupピボット要素から始まる線形検索によって見つかります。関連するピボット要素が配置されているキャッシュされたしゃっくりの前処理または増分更新からわかっている場合は、一定の時間でそこにジャンプできます。完全線形検索を実行する必要があった場合は、データ構造を更新して、これらの位置がしゃっくりデータでカバーされていることを保存します。これにより、次回は正しいピボット要素をすばやく特定できます。
しゃっくり検索
これは、前処理を行ったかどうかによって異なります。そうでない場合は、すべてのデータを線形検索します(この時点で前処理パーツを実行することもできます)。それ以外の場合、前処理されたデータ構造はしゃっくりデータの位置を示すことができ、これらを直接検索できます。つまり、しゃっくりデータの5%のみを使用して線形検索を実行します。
パフォーマンス
完全に前処理されたデータを使用した一時的な検索では、デフォルトのバイナリ検索とほぼ同じパフォーマンスが得られます。各ステップで単純な比較を行う代わりに、しゃっくり要素にヒットしたかどうかを判断するために追加の比較が必要であり、そうであれば実際のピボット要素を見つけるためにデータ構造にアクセスする必要があります。ただし、この追加作業は、データセットの半分だけを除外するだけでなく、しゃっくりデータ部分も除外するという事実によって、少し緩和されます。
もちろん、線形検索に頼らなければならない場合、これはそれに応じて低下します。
既存のしゃっくりに関する情報がなく、すべてのデータを直線的に検索する必要がある場合、しゃっくり検索は悪いケースです。
最後に、タイムスタンプを検索しないが、他の基準があり、そのようなログエントリが存在しない場合、これは最悪のケースです。実際、しゃっくりのデータ構造がない場合、両方の検索実行が同じしゃっくりの位置を線形にスキャンする可能性があるため(それでもO(n)ですが)、線形検索よりも遅くなります。
データ構造が利用可能で完全に前処理されている場合、最悪のケースのランタイムはO(max(log(M)* log(N)、M))になります。ここで、Mは線形検索されるヒカップデータの量です。また、O(log(M))のデータ構造からしゃっくり位置を指定して、しゃっくりデータの終わりを検索できると仮定します。