Cのローリングメディアンアルゴリズム


114

私は現在、Cでローリングメジアンフィルター(ローリングミーンフィルターに類似)を実装するアルゴリズムに取り組んでいます。私の文献検索から、それを行うには2つの合理的に効率的な方法があるようです。1つ目は、値の初期ウィンドウを並べ替えてから、バイナリ検索を実行して新しい値を挿入し、反復ごとに既存の値を削除します。

2番目(Hardle and Steiger、1995、JRSS-C、アルゴリズム296から)は、両端にヒープ、もう一方に最小ヒープ、中央に中央値を持つ両頭ヒープ構造を構築します。これにより、O(n log n)の代わりに線形時間アルゴリズムが生成されます。

これが私の問題です。前者の実装は可能ですが、これを何百万もの時系列で実行する必要があるため、効率が非常に重要です。後者は実装が非常に難しいことがわかっています。RのstatsパッケージのコードのTrunmed.cファイルでコードを見つけましたが、かなり判読できません。

線形時間ローリングメディアンアルゴリズムの適切に作成されたC実装を知っている人はいますか?

編集:Trunmed.cコードへのリンクhttp://google.com/codesearch/p?hl=en&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c


移動平均を実装しただけです...移動中央値はややトリッキーです。移動中央値をグーグルしてみてください。
マット

グーグルとグーグルコード検索を試してみました。Trunmed.cコードとTrunmedコードのSGIポート用の別の言語での実装が判明しました(私が知る限り)。また、私が引用したJRSSアルゴリズムは、元のコードがアーカイブされなかったジャーナルシリーズの唯一のアルゴリズムであることは明らかです。
AWB、

各時系列にはいくつの数字がありますか?100万個でも、数が数千しかない場合は、実行に1〜2分以上かかることはありません(コードが効率的に記述されている場合)。
Dana the Sane

16
2つのヒープソリューションはどのように線形ですか?これはO(n log k)です。kはウィンドウサイズです。ヒープの削除はO(log k)だからです。
yairchu 2009

3
いくつかの実装と比較:github.com/suomela/median-filter
Jukka Suomela '21 / 04/14

回答:


28

src/library/stats/src/Trunmed.cスタンドアロンのC ++クラス/ Cサブルーチンでも同様のものが欲しかったので、Rを数回見ました。これは実際には1つの2つの実装であることに注意してくださいsrc/library/stats/man/runmed.Rd(ヘルプファイルのソースを参照)。

\details{
  Apart from the end values, the result \code{y = runmed(x, k)} simply has
  \code{y[j] = median(x[(j-k2):(j+k2)])} (k = 2*k2+1), computed very
  efficiently.

  The two algorithms are internally entirely different:
  \describe{
    \item{"Turlach"}{is the Härdle-Steiger
      algorithm (see Ref.) as implemented by Berwin Turlach.
      A tree algorithm is used, ensuring performance \eqn{O(n \log
        k)}{O(n * log(k))} where \code{n <- length(x)} which is
      asymptotically optimal.}
    \item{"Stuetzle"}{is the (older) Stuetzle-Friedman implementation
      which makes use of median \emph{updating} when one observation
      enters and one leaves the smoothing window.  While this performs as
      \eqn{O(n \times k)}{O(n * k)} which is slower asymptotically, it is
      considerably faster for small \eqn{k} or \eqn{n}.}
  }
}

これがよりスタンドアロンで再利用されるのを見るとよいでしょう。ボランティアですか?Rビットのいくつかをお手伝いします。

編集1:上記の古いバージョンのTrunmed.cへのリンクの他に、現在のSVNコピーがあります

  • Srunmed.c (Stuetzleバージョンの場合)
  • Trunmed.c (Turlachバージョンの場合)
  • runmed.R これらを呼び出すR関数

編集2:Ryan Tibshiraniは、高速中央値ビニングに関するCおよびFortranコードをいくつか持っています。これは、ウィンドウ化アプローチの適切な出発点となる可能性があります。


ダークさん、ありがとう。クリーンなソリューションを取得したら、GPLでリリースすることを計画しています。RとPythonのインターフェースの設定にも興味があります。
AWB

9
@AWBこのアイデアで何が起こったのですか?ソリューションをパッケージに組み込みましたか?
Xu Wang、

20

注文統計を含むc ++データ構造の最新の実装を見つけることができなかったため、MAK(Match Editorial:FloatingMedianまでスクロールダウン)によって提案されたトップコーダーリンクに両方のアイデアを実装することになりました。

2つのマルチセット

最初のアイデアでは、挿入/削除ごとにO(ln N)を使用してデータを2つのデータ構造(ヒープ、マルチセットなど)に分割するため、大きなコストなしに変位値を動的に変更できません。つまり、ローリングメジアンまたはローリング75%を同時に取得できますが、両方を同時に実行することはできません。

セグメントツリー

2番目のアイデアは、挿入/削除/クエリに対してO(ln N)であるがより柔軟なセグメントツリーを使用します。何よりも「N」はデータ範囲のサイズです。したがって、ローリングメジアンに100万項目のウィンドウがあり、データが1..65536と異なる場合、100万のローリングウィンドウの移動ごとに必要な操作は16回だけです。

c ++コードは上記のDenisが投稿したものに似ています(「量子化データの単純なアルゴリズムは次のとおりです」)。

GNU Order Statistic Trees

あきらめる直前に、stdlibc ++に順序統計ツリーが含まれていることがわかりました!!!

これらには2つの重要な操作があります。

iter = tree.find_by_order(value)
order = tree.order_of_key(value)

libstdc ++マニュアルのpolicy_based_data_structures_test(「分割して結合」を検索)を参照してください。

私はc ++ 0x / c ++ 11スタイルの部分的なtypedefをサポートするコンパイラーの便利なヘッダーで使用するためにツリーをラップしました:

#if !defined(GNU_ORDER_STATISTIC_SET_H)
#define GNU_ORDER_STATISTIC_SET_H
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

// A red-black tree table storing ints and their order
// statistics. Note that since the tree uses
// tree_order_statistics_node_update as its update policy, then it
// includes its methods by_order and order_of_key.
template <typename T>
using t_order_statistic_set = __gnu_pbds::tree<
                                  T,
                                  __gnu_pbds::null_type,
                                  std::less<T>,
                                  __gnu_pbds::rb_tree_tag,
                                  // This policy updates nodes'  metadata for order statistics.
                                  __gnu_pbds::tree_order_statistics_node_update>;

#endif //GNU_ORDER_STATISTIC_SET_H

実際、libstdc ++拡張コンテナーは、設計上、複数の値を許可していません。上記の名前(t_order_statistic_set)で示唆されているように、複数の値がマージされます。したがって、彼らは私たちの目的のためにもう少し作業が必要です:-(
Leo Goodstadt

(セットではなく)カウントする値のマップを作成する必要があります2)ブランチサイズはキーの数を反映する必要があります(libstdc ++-v3 / include / ext / pb_ds / detail / tree_policy / order_statistics_imp.hpp)継承元ツリー、および3)オーバーロードinsert()カウントを増やす/値がすでに存在する場合はupdate_to_top()を呼び出す4)オーバーロードerase()カウントを減らす/値が一意でない場合はupdate_to_top()を呼び出す(libstdc ++-を参照) v3 / include / ext / pb_ds / detail / rb_tree_map_ / rb_tree_.hpp)任意のボランティア??
Leo Goodstadt

15

私がやったCの実装をここに。この質問には、さらにいくつかの詳細があります:C-Turlach実装のローリング中央値

使用例:

int main(int argc, char* argv[])
{
   int i,v;
   Mediator* m = MediatorNew(15);

   for (i=0;i<30;i++)
   {
      v = rand()&127;
      printf("Inserting %3d \n",v);
      MediatorInsert(m,v);
      v=MediatorMedian(m);
      printf("Median = %3d.\n\n",v);
      ShowTree(m);
   }
}

6
min-median-maxヒープに基づいた素晴らしく、高速で明確な実装。とても良い仕事です。
ヨハネスルドルフ

このソリューションのJavaバージョンを見つけるにはどうすればよいですか?
ヘンガメ2015

10

私はこの増分中央値推定量を使用します:

median += eta * sgn(sample - median)

これは、より一般的な平均推定量と同じ形式です。

mean += eta * (sample - mean)

ここで、etaは小さな学習率パラメーター(例:)0.001sgn()あり、の1つを返す符号関数です{-1, 0, 1}。(etaデータが非定常であり、時間の経過に伴う変化を追跡する場合は、このような定数を使用します。それ以外の場合は、定常ソースの場合、eta = 1 / n収束するようなものを使用します。ここnで、これまでに見られたサンプルの数です。)

また、任意の分位点で機能するように中央値推定量を変更しました。一般的には、クォン機能はあなたに2つの画分にデータを分割値を指示しますp1 - p。以下では、この値を段階的に見積もります。

quantile += eta * (sgn(sample - quantile) + 2.0 * p - 1.0)

pは以内でなければなりません[0, 1]。これは基本的に、sgn()関数の対称的な出力{-1, 0, 1}を片側に傾けるようにシフトし、データサンプルを2つの等しくないサイズのビンに分割します(データの分数pと分数1 - pは、それぞれ変位値の見積もりよりも小さいか大きい)。の場合p = 0.5、これは推定値の中央値に減少することに注意してください。


2
クール、移動平均に基づいて 'eta'を調整する変更を次に示します(平均は中央値の大まかな推定値として使用されるため、小さな値に収束するのと同じ割合で大きな値に収束します)。つまり、etaは自動的に調整されます。 stackoverflow.com/questions/11482529/…–
Jeff McClintock

3
同様の手法については、質素なストリーミングに関する次のペーパーを参照してください。arxiv.org / pdf / 1407.1121v1.pdf 四分位数を推定し、平均値の変化に適応できます。最後の見積もりと最後の調整の方向(+1または-1)の2つの値のみを保存する必要があります。アルゴリズムの実装は簡単です。エラーは5%以内、約97%の時間であることがわかりました。
Paul Chernoch

9

これは、量子化されたデータ(数か月後)の単純なアルゴリズムです。

""" median1.py: moving median 1d for quantized, e.g. 8-bit data

Method: cache the median, so that wider windows are faster.
    The code is simple -- no heaps, no trees.

Keywords: median filter, moving median, running median, numpy, scipy

See Perreault + Hebert, Median Filtering in Constant Time, 2007,
    http://nomis80.org/ctmf.html: nice 6-page paper and C code,
    mainly for 2d images

Example:
    y = medians( x, window=window, nlevel=nlevel )
    uses:
    med = Median1( nlevel, window, counts=np.bincount( x[0:window] ))
    med.addsub( +, - )  -- see the picture in Perreault
    m = med.median()  -- using cached m, summ

How it works:
    picture nlevel=8, window=3 -- 3 1s in an array of 8 counters:
        counts: . 1 . . 1 . 1 .
        sums:   0 1 1 1 2 2 3 3
                        ^ sums[3] < 2 <= sums[4] <=> median 4
        addsub( 0, 1 )  m, summ stay the same
        addsub( 5, 1 )  slide right
        addsub( 5, 6 )  slide left

Updating `counts` in an `addsub` is trivial, updating `sums` is not.
But we can cache the previous median `m` and the sum to m `summ`.
The less often the median changes, the faster;
so fewer levels or *wider* windows are faster.
(Like any cache, run time varies a lot, depending on the input.)

See also:
    scipy.signal.medfilt -- runtime roughly ~ window size
    http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c

"""

from __future__ import division
import numpy as np  # bincount, pad0

__date__ = "2009-10-27 oct"
__author_email__ = "denis-bz-py at t-online dot de"


#...............................................................................
class Median1:
    """ moving median 1d for quantized, e.g. 8-bit data """

    def __init__( s, nlevel, window, counts ):
        s.nlevel = nlevel  # >= len(counts)
        s.window = window  # == sum(counts)
        s.half = (window // 2) + 1  # odd or even
        s.setcounts( counts )

    def median( s ):
        """ step up or down until sum cnt to m-1 < half <= sum to m """
        if s.summ - s.cnt[s.m] < s.half <= s.summ:
            return s.m
        j, sumj = s.m, s.summ
        if sumj <= s.half:
            while j < s.nlevel - 1:
                j += 1
                sumj += s.cnt[j]
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        else:
            while j > 0:
                sumj -= s.cnt[j]
                j -= 1
                # print "j sumj:", j, sumj
                if sumj - s.cnt[j] < s.half <= sumj:  break
        s.m, s.summ = j, sumj
        return s.m

    def addsub( s, add, sub ):
        s.cnt[add] += 1
        s.cnt[sub] -= 1
        assert s.cnt[sub] >= 0, (add, sub)
        if add <= s.m:
            s.summ += 1
        if sub <= s.m:
            s.summ -= 1

    def setcounts( s, counts ):
        assert len(counts) <= s.nlevel, (len(counts), s.nlevel)
        if len(counts) < s.nlevel:
            counts = pad0__( counts, s.nlevel )  # numpy array / list
        sumcounts = sum(counts)
        assert sumcounts == s.window, (sumcounts, s.window)
        s.cnt = counts
        s.slowmedian()

    def slowmedian( s ):
        j, sumj = -1, 0
        while sumj < s.half:
            j += 1
            sumj += s.cnt[j]
        s.m, s.summ = j, sumj

    def __str__( s ):
        return ("median %d: " % s.m) + \
            "".join([ (" ." if c == 0 else "%2d" % c) for c in s.cnt ])

#...............................................................................
def medianfilter( x, window, nlevel=256 ):
    """ moving medians, y[j] = median( x[j:j+window] )
        -> a shorter list, len(y) = len(x) - window + 1
    """
    assert len(x) >= window, (len(x), window)
    # np.clip( x, 0, nlevel-1, out=x )
        # cf http://scipy.org/Cookbook/Rebinning
    cnt = np.bincount( x[0:window] )
    med = Median1( nlevel=nlevel, window=window, counts=cnt )
    y = (len(x) - window + 1) * [0]
    y[0] = med.median()
    for j in xrange( len(x) - window ):
        med.addsub( x[j+window], x[j] )
        y[j+1] = med.median()
    return y  # list
    # return np.array( y )

def pad0__( x, tolen ):
    """ pad x with 0 s, numpy array or list """
    n = tolen - len(x)
    if n > 0:
        try:
            x = np.r_[ x, np.zeros( n, dtype=x[0].dtype )]
        except NameError:
            x += n * [0]
    return x

#...............................................................................
if __name__ == "__main__":
    Len = 10000
    window = 3
    nlevel = 256
    period = 100

    np.set_printoptions( 2, threshold=100, edgeitems=10 )
    # print medians( np.arange(3), 3 )

    sinwave = (np.sin( 2 * np.pi * np.arange(Len) / period )
        + 1) * (nlevel-1) / 2
    x = np.asarray( sinwave, int )
    print "x:", x
    for window in ( 3, 31, 63, 127, 255 ):
        if window > Len:  continue
        print "medianfilter: Len=%d window=%d nlevel=%d:" % (Len, window, nlevel)
            y = medianfilter( x, window=window, nlevel=nlevel )
        print np.array( y )

# end median1.py

4

ローリングメジアンは、数値の2つのパーティションを維持することによって見つけることができます。

パーティションを維持するには、最小ヒープと最大ヒープを使用します。

最大ヒープには、中央値以下の数値が含まれます。

最小ヒープには、中央値以上の数値が含まれます。

バランス制約: 要素の総数が偶数の場合、両方のヒープに等しい要素が必要です。

要素の総数が奇数の場合、最大ヒープは最小ヒープよりも1つ多い要素になります。

中央値要素:両方のパーティションの要素数が等しい場合、中央値は最初のパーティションの最大要素と2番目のパーティションの最小要素の合計の半分になります。

そうでない場合、中央値は最初のパーティションの最大要素になります。

アルゴリズム-
1-2つのヒープ(1分のヒープと1つの最大のヒープ)を取る
   最大ヒープには要素の前半数が含まれます
   最小ヒープには、要素の後半の数が含まれます

2-ストリームからの新しい数値を最大ヒープのトップと比較し、 
   小さいか等しい場合は、その数を最大ヒープに追加します。 
   それ以外の場合は、最小ヒープに数値を追加します。

3-最小ヒープに最大ヒープより多くの要素がある場合 
   次に、最小ヒープの最上位要素を削除し、最大ヒープに追加します。
   最大ヒープに最小ヒープよりも複数の要素がある場合 
   次に、最大ヒープの最上位要素を削除し、最小ヒープを追加します。

4-両方のヒープの要素数が等しい場合
   中央値は、最大ヒープの最大要素と最小ヒープの最小要素の合計の半分になります。
   そうでない場合、中央値は最初のパーティションの最大要素になります。
public class Solution {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        RunningMedianHeaps s = new RunningMedianHeaps();
        int n = in.nextInt();
        for(int a_i=0; a_i < n; a_i++){
            printMedian(s,in.nextInt());
        }
        in.close();       
    }

    public static void printMedian(RunningMedianHeaps s, int nextNum){
            s.addNumberInHeap(nextNum);
            System.out.printf("%.1f\n",s.getMedian());
    }
}

class RunningMedianHeaps{
    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(Comparator.reverseOrder());

    public double getMedian() {

        int size = minHeap.size() + maxHeap.size();     
        if(size % 2 == 0)
            return (maxHeap.peek()+minHeap.peek())/2.0;
        return maxHeap.peek()*1.0;
    }

    private void balanceHeaps() {
        if(maxHeap.size() < minHeap.size())
        {
            maxHeap.add(minHeap.poll());
        }   
        else if(maxHeap.size() > 1+minHeap.size())
        {
            minHeap.add(maxHeap.poll());
        }
    }

    public void addNumberInHeap(int num) {
        if(maxHeap.size()==0 || num <= maxHeap.peek())
        {
            maxHeap.add(num);
        }
        else
        {
            minHeap.add(num);
        }
        balanceHeaps();
    }
}

3番目のJavaの回答がCの質問にどれほどの利益をもたらすかは、私にはわかりません。新しい質問をしてから、その質問にJavaの回答を入力してください。
jww

これを読んだ後、ロジックが停止し、「最小ヒープの最上位要素を削除して、最小ヒープを追加する」。少なくとも投稿する前にアルゴを読むように礼儀正しく
Cyclotron3x3

4
このアルゴリズムは、ローリングメジアンではなく、増加する要素のメジアンを対象としています。ローリングメジアンの場合、ヒープから要素を削除する必要もあります。これは、最初に見つける必要があります。
ウォルター

2

ストリーム内のすべての値が(比較的)定義された範囲内の整数である場合、単純で正確な解決策がある特別なケースがあることを指摘する価値があるかもしれません。たとえば、それらがすべて0〜1023の範囲にある必要があると仮定します。この場合は、1024要素の配列とカウントを定義し、これらの値をすべてクリアします。ストリームの各値について、対応するビンとカウントを増分します。ストリームが終了した後、count / 2の最も高い値を含むビンを見つけます。0から始まる連続するビンを追加することで簡単に達成できます。同じ方法を使用して、任意のランク順の値を見つけることができます。(実行中にビンの飽和状態を検出し、ストレージビンのサイズをより大きなタイプに「アップグレード」する必要がある場合は、少し複雑になります。)

この特別なケースは人為的に見えるかもしれませんが、実際には非常に一般的です。また、実数が範囲内にあり、「十分に良い」レベルの精度がわかっている場合は、実数の近似として適用できます。これは、「実世界」のオブジェクトのグループに対する測定のほとんどすべてのセットを保持します。たとえば、人々のグループの身長や体重。十分な大きさのセットではありませんか?地球上のすべての(個々の)バクテリアの長さや重さに対しても同様に機能します-誰かがデータを提供できると仮定します!

それは私がオリジナルを誤って読んだようです-それは非常に長いストリームのちょうど中央値の代わりにスライディングウィンドウ中央値を望んでいるようです。このアプローチはまだそのために機能します。最初のウィンドウの最初のN個のストリーム値を読み込み、次にN + 1番目のストリーム値に対して、対応するビンをインクリメントしながら、0番目のストリーム値に対応するビンをデクリメントします。この場合、最後のN値を保持して減分できるようにする必要があります。これは、サイズNの配列を周期的にアドレス指定することで効率的に実行できます。中央値の位置は-2、-1,0,1だけしか変更できないため、2スライディングウィンドウの各ステップで、各ステップの中央値までのすべてのビンを合計する必要はありません。どのサイドのビンが変更されたかに応じて「中央ポインター」を調整するだけです。例えば、新しい値と削除される値の両方が現在の中央値を下回る場合、値は変化しません(オフセット= 0)。この方法は、Nが大きくなりすぎてメモリに保持できなくなると機能しなくなります。


1

ある時点の関数として値を参照する機能がある場合は、値を置き換えてサンプリングし、ブートストラップを適用して、信頼区間内のブートストラップ中央値を生成できます。これにより、入力値をデータ構造に常にソートするよりも効率的に近似中央値を計算できます。


1

Javaで実行中の中央値を必要とする人のために... PriorityQueueはあなたの友達です。O(ログN)挿入、O(1)現在の中央値、およびO(N)削除。あなたがあなたのデータの分布を知っているなら、あなたはこれよりずっと良いことができます。

public class RunningMedian {
  // Two priority queues, one of reversed order.
  PriorityQueue<Integer> lower = new PriorityQueue<Integer>(10,
          new Comparator<Integer>() {
              public int compare(Integer arg0, Integer arg1) {
                  return (arg0 < arg1) ? 1 : arg0 == arg1 ? 0 : -1;
              }
          }), higher = new PriorityQueue<Integer>();

  public void insert(Integer n) {
      if (lower.isEmpty() && higher.isEmpty())
          lower.add(n);
      else {
          if (n <= lower.peek())
              lower.add(n);
          else
              higher.add(n);
          rebalance();
      }
  }

  void rebalance() {
      if (lower.size() < higher.size() - 1)
          lower.add(higher.remove());
      else if (higher.size() < lower.size() - 1)
          higher.add(lower.remove());
  }

  public Integer getMedian() {
      if (lower.isEmpty() && higher.isEmpty())
          return null;
      else if (lower.size() == higher.size())
          return (lower.peek() + higher.peek()) / 2;
      else
          return (lower.size() < higher.size()) ? higher.peek() : lower
                  .peek();
  }

  public void remove(Integer n) {
      if (lower.remove(n) || higher.remove(n))
          rebalance();
  }
}

c ++には、標準ライブラリの拡張機能として、gnuからの統計ツリーの順序があります。以下の私の投稿を参照してください。
Leo Goodstadt

あなたのコードはここに正しく置かれていないと思います。のような不完全な部分があります:}), higher = new PriorityQueue<Integer>();またはnew PriorityQueue<Integer>(10,。コードを実行できませんでした。
ヘンガメ2015

@Hengameh Javaはステートメントをセミコロンで終了します-改行はまったく問題ではありません。正しくコピーしていないはずです。
Matthewが

新しい質問をしてから、その質問にJavaの回答を入力してください。
jww

0

正確な出力が重要でない場合(表示目的など)に使用できるものを以下に示します。totalcountとlastmedianに加えてnewvalueが必要です。

{
totalcount++;
newmedian=lastmedian+(newvalue>lastmedian?1:-1)*(lastmedian==0?newvalue: lastmedian/totalcount*2);
}

page_display_timeなどの非常に正確な結果を生成します。

ルール:入力ストリームは、ページ表示時間の順でスムーズで、カウント数が多く(> 30など)、中央値がゼロ以外である必要があります。

例:ページ読み込み時間、800アイテム、10ms ... 3000ms、平均90ms、実際の中央値:11ms

30回の入力後、エラーの中央値は一般に20%以下(9ms..12ms)で、次第に小さくなります。800入力後の誤差は+ -2%です。

同様のソリューションを持つ別の思想家はここにあります:Median Filter超効率的な実装


-1

これがJavaの実装です

package MedianOfIntegerStream;

import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


public class MedianOfIntegerStream {

    public Set<Integer> rightMinSet;
    public Set<Integer> leftMaxSet;
    public int numOfElements;

    public MedianOfIntegerStream() {
        rightMinSet = new TreeSet<Integer>();
        leftMaxSet = new TreeSet<Integer>(new DescendingComparator());
        numOfElements = 0;
    }

    public void addNumberToStream(Integer num) {
        leftMaxSet.add(num);

        Iterator<Integer> iterMax = leftMaxSet.iterator();
        Iterator<Integer> iterMin = rightMinSet.iterator();
        int maxEl = iterMax.next();
        int minEl = 0;
        if (iterMin.hasNext()) {
            minEl = iterMin.next();
        }

        if (numOfElements % 2 == 0) {
            if (numOfElements == 0) {
                numOfElements++;
                return;
            } else if (maxEl > minEl) {
                iterMax.remove();

                if (minEl != 0) {
                    iterMin.remove();
                }
                leftMaxSet.add(minEl);
                rightMinSet.add(maxEl);
            }
        } else {

            if (maxEl != 0) {
                iterMax.remove();
            }

            rightMinSet.add(maxEl);
        }
        numOfElements++;
    }

    public Double getMedian() {
        if (numOfElements % 2 != 0)
            return new Double(leftMaxSet.iterator().next());
        else
            return (leftMaxSet.iterator().next() + rightMinSet.iterator().next()) / 2.0;
    }

    private class DescendingComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

    public static void main(String[] args) {
        MedianOfIntegerStream streamMedian = new MedianOfIntegerStream();

        streamMedian.addNumberToStream(1);
        System.out.println(streamMedian.getMedian()); // should be 1

        streamMedian.addNumberToStream(5);
        streamMedian.addNumberToStream(10);
        streamMedian.addNumberToStream(12);
        streamMedian.addNumberToStream(2);
        System.out.println(streamMedian.getMedian()); // should be 5

        streamMedian.addNumberToStream(3);
        streamMedian.addNumberToStream(8);
        streamMedian.addNumberToStream(9);
        System.out.println(streamMedian.getMedian()); // should be 6.5
    }
}

新しい質問をしてから、その質問にJavaの回答を入力してください。
jww

-4

平滑化された平均が必要な場合は、最新の値にxを乗算し、平均値に(1-x)を乗算してから、それらを加算するのが簡単です。これが新しい平均になります。

編集:ユーザーが要求したものではなく、統計的に有効ではありませんが、多くの用途には十分です。
私はここに(反対票にもかかわらず)検索のために残しておきます!


2
これは平均を計算します。彼は中央値を望んでいます。また、セット全体ではなく、値のスライディングウィンドウの中央値を計算しています。
A.レビー

1
これは、Xに応じて減衰定数を持つ値のウィンドウの移動平均を計算します。これは、パフォーマンスが重要でカルマンフィルターを実行する必要がない場合に非常に役立ちます。検索で見つけられるように入れました。
マーティンベケット

これは、オーディオアプリ用の非常に基本的で安価なローパスフィルターとしてこのようなフィルターを実装したことも、すぐに思いついたものです。
James Morris、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.