C의 롤링 중앙값 알고리즘


114

저는 현재 C에서 롤링 메디안 필터 (롤링 평균 필터와 유사)를 구현하는 알고리즘을 연구하고 있습니다. 문헌 검색에서 두 가지 합리적으로 효율적인 방법이있는 것 같습니다. 첫 번째는 값의 초기 창을 정렬 한 다음 이진 검색을 수행하여 새 값을 삽입하고 각 반복에서 기존 값을 제거하는 것입니다.

두 번째 (Hardle and Steiger, 1995, JRSS-C, Algorithm 296에서)는 한쪽 끝에 maxheap, 다른 쪽 끝에 minheap, 중간에 중앙값이있는 양단 힙 구조를 빌드합니다. 이것은 O (n log n) 인 대신 선형 시간 알고리즘을 생성합니다.

내 문제는 다음과 같습니다. 전자를 구현하는 것은 가능하지만 수백만 개의 시계열에서 실행해야하므로 효율성이 매우 중요합니다. 후자는 구현하기가 매우 어렵습니다. R의 통계 패키지에 대한 코드의 Trunmed.c 파일에서 코드를 찾았지만 해독 할 수 없습니다.

선형 시간 롤링 중앙값 알고리즘에 대해 잘 작성된 C 구현을 아는 사람이 있습니까?

수정 : Trunmed.c 코드 링크 http://google.com/codesearch/p?hl=ko&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c


이동 평균을 구현했습니다. 중앙값 이동은 다소 까다 롭습니다. 이동 중앙값을 검색해보십시오.
Matt

Google 및 Google 코드 검색을 시도했습니다. Trunmed.c 코드와 Trunmed 코드의 SGI 포트에 대한 다른 언어로 구현되었습니다. 또한 내가 인용 한 JRSS 알고리즘은 저널 시리즈에서 원본 코드가 보관되지 않은 유일한 알고리즘입니다.
AWB

각 시계열에 몇 개의 숫자가 있습니까? 100 만 개라도 수천 개의 숫자 만 있으면 실행하는 데 1 ~ 2 분 이상 걸리지 않을 수 있습니다 (코드가 효율적으로 작성된 경우).
Dana the Sane

16
두 힙 솔루션은 어떻게 선형입니까? O (n log k)입니다. 여기서 k는 힙의 삭제가 O (log k)이기 때문에 창 크기입니다.
yairchu

답변:


28

src/library/stats/src/Trunmed.c독립 실행 형 C ++ 클래스 / C 서브 루틴에서도 비슷한 것을 원했기 때문에 R을 몇 번 살펴 보았습니다 . 이것은 실제로 하나의 두 가지 구현 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 사본이 있습니다.

편집 2 : Ryan Tibshirani에는 빠른 중앙값 비닝 에 대한 C 및 Fortran 코드 가 있으며 이는 창 접근 방식에 적합한 시작점이 될 수 있습니다.


고마워 더크. 깨끗한 솔루션을 얻으면 GPL로 출시 할 계획입니다. R 및 Python 인터페이스 설정에도 관심이 있습니다.
AWB

9
@AWB이 아이디어로 무슨 일이 일어 났습니까? 솔루션을 패키지에 통합 했습니까?
Xu Wang

20

주문 통계가있는 C ++ 데이터 구조의 현대적인 구현을 찾을 수 없으므로 MAK에서 제안한 상위 코더 링크에서 두 가지 아이디어를 모두 구현하게되었습니다 ( Match Editorial : FloatingMedian으로 스크롤).

두 개의 멀티 세트

첫 번째 아이디어는 삽입 / 삭제 당 O (ln N)를 사용하여 데이터를 두 개의 데이터 구조 (힙, 다중 집합 등)로 분할하므로 큰 비용없이 Quantile을 동적으로 변경할 수 없습니다. 즉, 롤링 중앙값 또는 롤링 75 %를 가질 수 있지만 동시에 둘 다 가질 수는 없습니다.

세그먼트 트리

두 번째 아이디어는 삽입 / 삭제 / 쿼리에 대해 O (ln N) 인 세그먼트 트리를 사용하지만 더 유연합니다. 모든 "N"의 가장 좋은 점은 데이터 범위의 크기입니다. 따라서 롤링 중앙값에 백만 항목의 창이 있지만 데이터가 1..65536에서 다른 경우 롤링 창 1 백만 이동 당 16 개의 작업 만 필요합니다 !!

C ++ 코드는 Denis가 위에 게시 한 것과 유사합니다 ( "양자화 된 데이터에 대한 간단한 알고리즘이 있습니다").

GNU 주문 통계 트리

포기하기 직전에 stdlibc ++에 주문 통계 트리가 포함되어 있음을 발견했습니다 !!!

여기에는 두 가지 중요한 작업이 있습니다.

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)에서 제안한대로 여러 값이 병합됩니다. 그래서, 그들은 :-( 우리의 목적을 위해 조금 더 많은 작업을 필요
레오 Goodstadt

1) 계수 할 값의 맵 (세트 대신)을 만들어야합니다. 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 2012-06-27

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 힙을 기반으로 한 훌륭하고 빠르고 명확한 구현. 아주 잘 했어요.
Johannes Rudolph 2011

이 솔루션의 Java 버전을 어떻게 찾을 수 있습니까?
Hengameh

10

이 증분 중앙값 추정기를 사용합니다.

median += eta * sgn(sample - median)

더 일반적인 평균 추정치와 동일한 형식을가집니다.

mean += eta * (sample - mean)

여기 ETA는 작은 학습율 파라미터 (예이다 0.001), 및 sgn()의 하나를 리턴 시그넘 함수이다 {-1, 0, 1}. ( eta데이터가 고정되지 않고 시간에 따른 변화를 추적 하려면 이와 같은 상수를 사용하십시오 . 그렇지 않으면 고정 된 소스의 경우 eta = 1 / n수렴 과 같은 것을 사용하십시오 .n 지금까지 본 샘플 수는 하십시오.)

또한 임의의 분위수에 대해 작동하도록 중앙 추정치를 수정했습니다. 일반적으로 분위수 함수 는 데이터를 두 개의 분수, p및 으로 나누는 값을 알려줍니다 1 - p. 다음은이 값을 점진적으로 추정합니다.

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

p은 이내 여야합니다 [0, 1]. 이것은 본질적으로 sgn()함수의 대칭 출력 {-1, 0, 1}을 한쪽으로 기울 이도록 이동 하여 데이터 샘플을 두 개의 다른 크기의 빈으로 분할합니다 (데이터의 분수 p1 - p데이터가 각각 분위수 추정값보다 작거나 큼). 의 p = 0.5경우 이는 중앙 추정치로 감소합니다.


2
멋지다. 실행 평균을 기준으로 'eta'를 조정하는 수정이 있습니다 ... (평균은 중간 값의 대략적인 추정치로 사용되므로 작은 값에 수렴하는 것과 동일한 속도로 큰 값에 수렴합니다). 즉, eta는 자동으로 조정됩니다. stackoverflow.com/questions/11482529/...
제프 맥클린 톡을

3
유사한 기술에 대해서는 절약형 스트리밍에 대한이 문서를 참조하십시오. arxiv.org/pdf/1407.1121v1.pdf 모든 사 분위수를 추정 할 수 있으며 평균의 변화에 ​​적응합니다. 마지막 추정값과 마지막 조정 방향 (+1 또는 -1)의 두 값만 저장하면됩니다. 알고리즘은 구현이 간단합니다. 오류가 약 97 %의 5 % 이내라는 것을 알았습니다.
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

롤링 중앙값은 두 개의 숫자 분할을 유지하여 찾을 수 있습니다.

파티션을 유지하려면 Min Heap 및 Max Heap을 사용하십시오.

최대 힙에는 중앙값보다 작은 숫자가 포함됩니다.

최소 힙에는 중앙값보다 큰 숫자가 포함됩니다.

Balancing Constraint : 총 요소 수가 짝수이면 두 힙 모두 동일한 요소를 가져야합니다.

총 요소 수가 홀수이면 최대 힙은 최소 힙보다 하나 더 많은 요소를 갖게됩니다.

중앙값 요소 : 두 파티션에 동일한 수의 요소가있는 경우 중앙값은 첫 번째 파티션의 최대 요소와 두 번째 파티션의 최소 요소 합계의 절반이됩니다.

그렇지 않으면 중앙값이 첫 번째 파티션의 최대 요소가됩니다.

연산-
1- 두 개의 힙 가져 오기 (1 Min Heap 및 1 Max Heap)
   Max Heap은 전반부 요소 수를 포함합니다.
   최소 힙에는 후반 요소 수가 포함됩니다.

2- 스트림의 새 수를 Max Heap의 상단과 비교하십시오. 
   더 작거나 같으면 최대 힙에 해당 숫자를 추가하십시오. 
   그렇지 않으면 Min Heap에 숫자를 추가하십시오.

3- 최소 힙에 최대 힙보다 많은 요소가있는 경우 
   그런 다음 Min Heap의 최상위 요소를 제거하고 Max Heap을 추가합니다.
   최대 힙에 최소 힙보다 둘 이상의 요소가있는 경우 
   그런 다음 Max Heap의 최상위 요소를 제거하고 Min Heap을 추가하십시오.

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();
    }
}

세 번째 Java 답변이 C 질문에 얼마나 많은 이점을 제공하는지 명확하지 않습니다. 새로운 질문을 한 다음 해당 질문에 Java 답변을 제공해야합니다.
jww

논리는 이것을 읽은 후 'Min Heap의 최상위 요소를 제거하고 Min Heap을 추가하십시오.' . 적어도 게시하기 전에 알고리즘을 읽을 수있는 예의가 있어야합니다
Cyclotron3x3

4
이 알고리즘은 롤링 중앙값이 아니라 증가하는 요소 수의 중앙값을위한 것입니다. 롤링 중앙값의 경우 먼저 발견해야하는 힙에서 요소를 제거해야합니다.
Walter

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 (log 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 2012-06-27

귀하의 코드가 여기에 올바르게 입력되지 않은 것 같습니다. 이 약간의 불완전한 부분이 같이있다 : }), higher = new PriorityQueue<Integer>();new PriorityQueue<Integer>(10,. 코드를 실행할 수 없습니다.
Hengameh

@Hengameh Java는 세미콜론으로 문을 종료합니다. 줄 바꿈은 전혀 중요하지 않습니다. 잘못 복사 했어야합니다.
Matthew Read

새로운 질문을 한 다음 해당 질문에 Java 답변을 제공해야합니다.
jww

0

다음은 정확한 출력이 중요하지 않을 때 사용할 수있는 것입니다 (디스플레이 목적 등). totalcount 및 lastmedian과 함께 newvalue가 필요합니다.

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

page_display_time과 같은 것에 대해 매우 정확한 결과를 생성합니다.

규칙 : 입력 스트림은 페이지 표시 시간의 순서에 따라 매끄럽고 개수가 많고 (> 30 등) 중앙값이 0이 아니어야합니다.

예 : 페이지로드 시간, 800 개 항목, 10ms ... 3000ms, 평균 90ms, 실제 중앙값 : 11ms

30 개 입력 후 중앙값 오류는 일반적으로 <= 20 % (9ms..12ms)이며 점점 더 작아집니다. 800 개 입력 후 오류는 + -2 %입니다.

비슷한 솔루션을 가진 또 다른 사상가가 있습니다. 중앙값 필터 매우 효율적인 구현


-1

다음은 자바 구현입니다.

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. Levy

1
이것은 X에 따라 감쇠 상수가있는 값 창의 실행 평균을 계산합니다. 성능이 중요하고 칼만 필터를 수행하는 데 신경 쓸 필요가없는 경우 매우 유용합니다. 검색 할 수 있도록 입력했습니다.
Martin Beckett

이것은 오디오 앱을위한 매우 기본적이고 값싼 저역 통과 필터와 같은 필터를 구현 한 즉시 생각했던 것입니다.
James Morris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.