web-dev-qa-db-ja.com

FIFOローリングメディアンの最小-最大-ヒープ

私はc ++でハードリアルタイム制約のあるシステムで作業していて、サイズN = 100から300の一連の数値のローリング/移動/ストリーミングの中央値を非常に高速に計算する方法が必要です。通常、このサイズは簡単です。ただし、この場合、アルゴリズムは0.1ミリ秒あたり約1000/2000回実行されます

すべての計算では、範囲(0-1)の単一の値が追加されるため、(コンテナーに応じて)以前の値は既に(弱く)ソートされています。

要件:

中央値ウィンドウは固定サイズであり、割り当てに時間がかかりすぎるため、動的メモリ割り当てはありません

最後に追加されたものを削除して新しい値を挿入するためのFIFOの動作

現在のアプローチ

現在、最も古い要素への参照を保持し、すべての要素が次に古い要素への参照を持っている最小-最大ヒープローリングメジアンアプローチを検討しています。ただし、ヒープ内の任意の位置からその要素を削除する必要があり、2つのヒープのいずれかにある可能性があるため、これがどのように機能するかはわかりません。

7
Bluefarmer

これを行うつもりなら、各ノードが左側のサブツリーのサイズを追跡している(そして全体のサイズを追跡している)バランスのとれたツリー(たとえば、AVLまたは赤黒)を使用するでしょう。 。これは、データ自体を格納する循環バッファへのインデックスとして機能します。

したがって、新しいアイテムが到着すると、循環バッファー内で最も古い値が見つかります。ツリーでそのノードを検索し、それを削除します[O(log N)複雑さ]。ツリーを下降して削除するときに、各ノードのカウントを更新して、どのサブツリーが小さくなっているのかを示します。

循環バッファー内のそのノードを新しい値で上書きします。新しい値[またO(log N)複雑さ]のノードをツリーに挿入します。ここでも、ツリーを下降して新しい値を挿入するときに、ノードのカウントを更新して新しいサブツリーのサイズを示します。

ツリーを介して中央値を見つけます-ルートノードから開始します。左側のサブツリーのサイズを確認して、中央値が左側または右側のサブツリーにあるかどうかを判断します。中央値に達するまで、木を下って行きます。ツリーの深さはlog Nに比例するため、この操作もO(log N)です。

細かい点が1つ残っています。ツリーは通常、動的割り当てを使用します。ただし、これが問題になるのを簡単に防ぐことができます。必要なすべてのツリーノードにスペースのブロックを事前に割り当てます。それらを(たとえば)リンクされた空きノードのリストに構築します。ノードを割り当てる必要がある場合は、常にリストの先頭からノードを取得するだけです。ノードを解放する必要がある場合は、ノードをリストの先頭に追加するだけです。

ツリーが「いっぱい」になったら、基本的にリストにノードを追加し、すぐにそれを再利用して新しいノードを作成します。 「リンクされたリスト」は、基本的には、そのフリーの間、その1つのノードへのポインタとして機能します。

1
Jerry Coffin

これはジェリー・コフィンのアイデアを洗練させたものです。

  • すべてのノードが循環バッファーに直接存在する、ほぼバランスの取れたツリーを使用します。
  • サイズを常に一定に保つために、ダミー値で初期化します。
  • 各ノードでleftrightおよびparentポインター(またはインデックス)を使用します。
  • 各ノードにrankを保存して管理します。
  • 要素の挿入を削除する代わりに、値を変更するだけでインプレース置換を行います。
  • 交換後、通常、ツリーは注文されなくなります。
  • ローテーションを使用して、次のような順序に戻します
    • 順序に違反するノードは常に多くても1つです。
    • ツリーはそのRBまたはAVLプロパティを保持します。

これを行うと、ノードを削除して挿入するよりも確実に速くなります。運が良ければ、ノードは数回しか移動しません。確率が50%の場合、古い要素と新しい要素は同じハーフツリーに属します。

回転を工夫する必要がありますが、順序を復元し、同時にツリーのバランスを保つ必要があるため、これは少し複雑になる可能性があります。

この方法で4倍にできると思いますが、それは単なる推測です。

1
maaartinus