私はc ++でハードリアルタイム制約のあるシステムで作業していて、サイズN = 100から300の一連の数値のローリング/移動/ストリーミングの中央値を非常に高速に計算する方法が必要です。通常、このサイズは簡単です。ただし、この場合、アルゴリズムは0.1ミリ秒あたり約1000/2000回実行されます
すべての計算では、範囲(0-1)の単一の値が追加されるため、(コンテナーに応じて)以前の値は既に(弱く)ソートされています。
中央値ウィンドウは固定サイズであり、割り当てに時間がかかりすぎるため、動的メモリ割り当てはありません
最後に追加されたものを削除して新しい値を挿入するためのFIFOの動作
現在、最も古い要素への参照を保持し、すべての要素が次に古い要素への参照を持っている最小-最大ヒープローリングメジアンアプローチを検討しています。ただし、ヒープ内の任意の位置からその要素を削除する必要があり、2つのヒープのいずれかにある可能性があるため、これがどのように機能するかはわかりません。
これを行うつもりなら、各ノードが左側のサブツリーのサイズを追跡している(そして全体のサイズを追跡している)バランスのとれたツリー(たとえば、AVLまたは赤黒)を使用するでしょう。 。これは、データ自体を格納する循環バッファへのインデックスとして機能します。
したがって、新しいアイテムが到着すると、循環バッファー内で最も古い値が見つかります。ツリーでそのノードを検索し、それを削除します[O(log N)
複雑さ]。ツリーを下降して削除するときに、各ノードのカウントを更新して、どのサブツリーが小さくなっているのかを示します。
循環バッファー内のそのノードを新しい値で上書きします。新しい値[またO(log N)
複雑さ]のノードをツリーに挿入します。ここでも、ツリーを下降して新しい値を挿入するときに、ノードのカウントを更新して新しいサブツリーのサイズを示します。
ツリーを介して中央値を見つけます-ルートノードから開始します。左側のサブツリーのサイズを確認して、中央値が左側または右側のサブツリーにあるかどうかを判断します。中央値に達するまで、木を下って行きます。ツリーの深さはlog Nに比例するため、この操作もO(log N)
です。
細かい点が1つ残っています。ツリーは通常、動的割り当てを使用します。ただし、これが問題になるのを簡単に防ぐことができます。必要なすべてのツリーノードにスペースのブロックを事前に割り当てます。それらを(たとえば)リンクされた空きノードのリストに構築します。ノードを割り当てる必要がある場合は、常にリストの先頭からノードを取得するだけです。ノードを解放する必要がある場合は、ノードをリストの先頭に追加するだけです。
ツリーが「いっぱい」になったら、基本的にリストにノードを追加し、すぐにそれを再利用して新しいノードを作成します。 「リンクされたリスト」は、基本的には、そのフリーの間、その1つのノードへのポインタとして機能します。
これはジェリー・コフィンのアイデアを洗練させたものです。
left
、right
およびparent
ポインター(またはインデックス)を使用します。rank
を保存して管理します。これを行うと、ノードを削除して挿入するよりも確実に速くなります。運が良ければ、ノードは数回しか移動しません。確率が50%の場合、古い要素と新しい要素は同じハーフツリーに属します。
回転を工夫する必要がありますが、順序を復元し、同時にツリーのバランスを保つ必要があるため、これは少し複雑になる可能性があります。
この方法で4倍にできると思いますが、それは単なる推測です。