3
スライディングウィンドウの中央値を計算するための非自明なアルゴリズム
実行中の中央値を計算する必要があります: 入力: nnn、kkk、vector (x1,x2,…,xn)(x1,x2,…,xn)(x_1, x_2, \dotsc, x_n)。 出力: vector (y1,y2,…,yn−k+1)(y1,y2,…,yn−k+1)(y_1, y_2, \dotsc, y_{n-k+1})、ここでyiyiy_iはの中央値です(xi,xi+1,…,xi+k−1)(xi,xi+1,…,xi+k−1)(x_i, x_{i+1}, \dotsc, x_{i+k-1})。 (近似による不正行為はありません。正確な解を求めます。要素xixix_iは大きな整数です。) サイズ探索木を維持する簡単なアルゴリズムがありkkkます。合計実行時間はO(nlogk)O(nlogk)O(n \log k)です。(ここで「検索ツリー」とは、対数時間での挿入、削除、および中央値クエリをサポートする効率的なデータ構造を指します。) しかし、これは私には少し愚かなようです。中央値だけでなく、サイズkのすべてのウィンドウ内のすべての順序統計を効果的に学習します。さらに、特にkが大きい場合、これは実際にはあまり魅力的ではありません(大きな検索ツリーが遅くなる傾向があり、メモリ消費のオーバーヘッドが自明ではなく、キャッシュ効率が悪いことが多いなど)。kkkkkk 大幅に改善できることはありますか? 下限はありますか(たとえば、単純なアルゴリズムは比較モデルに漸近的に最適ですか)。 編集:デビッドエップスタインは、比較モデルのための素敵な下限を与えました!それにもかかわらず、些細なアルゴリズムよりも少し賢いことをすることは可能だろうか? たとえば、これらの線に沿って何かを行うことができます。入力ベクトルをサイズ部分に分割します。各部分をソートします(各要素の元の位置を追跡します)。そして、区分的にソートされたベクトルを使用して、補助データ構造なしで実行中の中央値を効率的に見つけますか?もちろん、これはまだO (n log kkkkですが、実際には、配列のソートは検索ツリーを維持するよりもはるかに高速になる傾向があります。O(nlogk)O(nlogk)O(n \log k) 編集2: Saeedは、検索ツリー操作よりもソートの方が速いと思う理由をいくつか見たいと思っていました。以下は、、n = 10 8の非常に簡単なベンチマークです。k=107k=107k = 10^7n=108n=108n = 10^8 ≈8s:それぞれk個の要素を持つベクトルをソートn/kn/kn/kkkk ≈10s:要素を持つベクトルの並べ替えnnn ≈80s:サイズkのハッシュテーブルでの挿入と削除nnnkkk ≈390s:サイズkのバランスの取れた検索ツリーでの挿入と削除nnnkkk ハッシュテーブルは比較のためだけにあります。このアプリケーションでは直接使用しません。 要約すると、ソートとバランスの取れた検索ツリー操作のパフォーマンスにはほぼ50倍の差があります。そして、を増やすと事態はさらに悪化します。kkk (技術的詳細:データ=ランダムな32ビット整数。コンピューター=典型的な最新のラップトップ。テストコードはC ++で記述され、標準ライブラリルーチン(std :: sort)とデータ構造(std :: …