C의 롤링 중앙값 알고리즘

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

두 번째는 한쪽 끝에는 최대 힙, 다른 쪽 끝에는 최소 힙, 중간에 중앙값이 있는 양단 힙 구조를 구축하는 것입니다(Hardle and Steiger, 1995, JRSS-C, 알고리즘 296에서 발췌). 이렇게 하면 O(n 로그 n)인 알고리즘 대신 선형 시간 알고리즘이 생성됩니다.

전자를 구현하는 것은 가능하지만 수백만 개의 시계열에서 실행해야 하므로 효율성이 매우 중요합니다. 후자는 구현하기가 매우 어렵다는 것이 증명되고 있습니다. R의 통계 패키지 코드의 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

질문에 대한 의견 (7)

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

39 는 현대 구현, 내가 couldn& 찾기 때문에 위 링크를 통해 아이디어를 구현하는 c++ 데이터 구조를 모두 오더할 통계 됐지 mak 제안한 코더 ([해당되어서는 사설] [1]: 로 스크롤하십시오 플로팅메디안).

중복집합 # 2

아이디어를 처음 두 개의 데이터 구조 (힙, 중복집합 등) 로 데이터를 파티션당 함께 O (log N) 로 변경할 수 없는 큰 비용 없이 무중단으로 분위수 삽입하십시오 / 삭제. I. e. 둘 수 있습니다, 또는 롤링 (rolling 마드얀의 75% 동시에건간에.

# 세그먼트마다 진단트리는

O (log N) 의 두 번째 idea 는 세그먼트마다 진단트리는 원하는거요 삽입 / 삭제 / 쿼리합니다 하지만 더 유연하다. 무엇보다도 이 &quot N"; 데이터의 크기가 범위. 그래서 데이터의 경우 a 는 롤링 마드얀의 접할 수 있지만, 고작 16 만개 품목을 1.65536 다릅니다 운동 후 당 1 만 창이 운영체 필요합니다!!

C++ 코드는 어떤 데니스 게시하기를 비슷한 위 (# 39 에 대한 간단한 알고리즘입니다 양자화 data&quot Here& ";)

# GNU 오더할 진단트리 통계

전에 내가 그냥 포기하는 것으로, 통계 주문하십시오 stdlibc++ map_layer 진단트리!!!

이러한 두 가지 중요한 작업:

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

참조 [libstdc++ 수작업식 policy_based_data_structures_test] [2] (검색하겠습니다 분할 및 join&quot ";).

나는 편의를 위해 관련 c++0x/c++11 랩된 나무를요 사용하기 위해 헤더입니다 컴파일러에도 스타일 부분 메리 페데프스:

#if !defined(GNU_ORDER_STATISTIC_SET_H)
#define GNU_ORDER_STATISTIC_SET_H
#include 
#include 

// 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 
using t_order_statistic_set = __gnu_pbds::tree<
                                  T,
                                  __gnu_pbds::null_type,
                                  std::less,
                                  __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

[1]: http://www.topcoder.com/tc, d1, d2 = = = match_editorials&amp srm310 모듈에서는 Static&amp? [2]: ++/manual/policy_based_data_structures_test.html http://gcc.gnu.org/onlinedocs/libstdc

해설 (2)

39 는 i&, ve done [C 구축상의] [1] [here] [2]. 몇 차례 더 자세한 내용은 이 질문: https://stackoverflow.com/questions/5527437/rolling-median-in-c-turlach-implementation.

사용 예제:


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

   for (i=0;i
해설 (2)

내가 사용하는 이 변동분 마드얀의 예측기를:

median += eta * sgn(sample - median)

더 일반적인 디스크입니다. diskid 같은 형태의 짓궂군요 예측기를:

mean += eta * (sample - mean)
  • 여기서 * eta 는 작은 com/go/learn_fl_cs4_learningas2_kr 전송률인 매개변수입니다 sgn () '및' (예를 들어 '0.001') 는 - 1, 0, 1} {시그넘 반환하는 함수 중 하나가 ''. (상수를 사용하여 '바스크 조국과 자유' 는 시간에 따라 변하며, 추적하려는 경우 이 같은 비 정상 및 데이터 그렇지 않으면 위한 정지 소스뿐만 사용하여 같은 일이 '에타 (η) = 1 / n' 통합한 'n' 은 지금까지 볼 수 있는 샘플링합니다.)

역시 내가 일할 수 있도록 예측기를 분위수 마드얀의 수정한 것이다. 일반적으로 [분위수 함수은] (http://en.wikipedia.org/wiki/Quantile_function) 로 데이터를 분할합니다 분수 있는 두 개의 값을 알려줍니다. 'p' 와 '1 - p'. 이 값을 변동분 다음 수 있을 것으로 기대하고 있다.

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

' [0, 1]' 가치 'p' 내에 있어야 합니다. 기본적으로 'sgn () 의 대칭' 이 부족의 function& 1, 0, 1} {- # 39 출력입니다 '라고 한 쪽을 향해' 의 두 부분으로 데이터를 파티셔닝에는 샘플링합니다 옵션을 크기의 장치함 (분수 'p' 와 '1 - p' 의 데이터 리스토어가 미만임 / 보다 분위수 추정치입니다 각각). 참고로, 이를 통해 '에 대한' p = 0.5 마드얀의 예측기를.

해설 (2)

39 에 대한 간단한 알고리즘입니다 here& 양자화 데이터 (달):

""" 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  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
해설 (0)

두 숫자의 파티션당 롤링 마드얀의 유지하여 찾을 수 있습니다.

파티션당 최대 힙 힙 및 유지에 필요한 최소 사용합니다.

  • 최대 힙 median.* 같은 보다 작은 숫자임 포함됩니다.

  • 최소 힙 포함될 보다 큰 숫자를 median.* 같음

  • 밸런싱과 Constraint:* 그런데도 요소를 모두 같을 경우 총 요소는 힙 있어야 합니다.

그리고 하나 더 이상 할 경우 최대 힙 홀수입니다 총 요소는 요소 최소 힙.

  • 는 모두 같은 수의 절반 이상이 될 경우 마드얀의 Element:* 파티션당 요소를 더한 후 마드얀의 에서 두 번째 요소 및 최소 1 에서 최대 요소점 파티션에만 파티션에만.

그렇지 않으면 마드얀의 적립율은 맥스야 요소점 에서 첫 번째 파티션. &lt pre>; 알고리즘입니다 - 1 - 2 시행하십시오 힙 (1 분 최대 힙 힙 (heap) 과 1) 올 상반기 최대 힙 (heap) 에는 여러 요소를 하반기 여러 요소를 포함할 수 최소 힙

2 - 비교하십시오 숫자를 통해 새로운 스트리밍합니다 상단형 최대 힙, 같거나 작을 경우 추가 그 수를 최대 힙. 그렇지 않으면 추가 숫자를 최소 힙.

이 보다 더 많은 요소가 최대 힙 힙 (heap) 는 3 분 최대 최소 힙 힙 및 주요 요소 제거 후 추가. 만약 최대 힙 둘 이상의 요소점 비해 최소 힙 최대 최소 힙 힙 및 주요 요소 제거 후 추가.

4 -) 는 여러 요소를 모두 같을 경우 다음 힙 최대 힙 합을 통해 최대 요소 및 최소 절반이 될 마드얀의 요소점 에서 최소 힙. 그렇지 않으면 마드얀의 적립율은 맥스야 요소점 시작한 첫 파티션에만. &lt /pre>;


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 minHeap = new PriorityQueue();
    PriorityQueue maxHeap = new PriorityQueue(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 
해설 (3)

그것은 아마 가치가 있다는 것을 지적하고, 특별한 경우에는 디스크입니다. diskid 간단한 그 라그랑지언은 다음과 같다: 모든 정수 (상대적으로) 의 값을 정의할 때 작은 범위 내에서 스트리밍합니다 있다. 예를 들어, 1023년 모든 0-사이의 방황케 solaris. 한다. 이 경우 그냥 배열입니다 요소 및 횟수, 선택해제합니다 정의하십시오 1024년 이러한 값이 모두. 각 값이 증분할 스트리밍합니다 .해당 용지함 및 카운트입니다. 이후 가장 높은 값을 포함하는 count/2 motor1.1end 스트리밍합니다 용지함 찾을 수 있습니다 - 0 부터 시작하여 연속적인 장치함 추가하기만 동일한 방법으로 자의적 랭크 오더할 가치를 찾을 수 있습니다. (감지할 수 있을 경우, 채도와 &quot 경미함 합병증 용지함 upgrading&quot. 크기가 큰 유형: 저장 장치함 실행 중에 필요하게 됩니다.)

이 특수한 경우가 있지만, 실제로는 매우 일반적인 인공 보일 수 있습니다. 또한 실제 적용할 수 있는 범위 내에 위치한 근사화를 표시됨과 숫자임 슬래시나 및 &quot 优秀 enough"; 수준의 precisiontm 알려져 있다. 이 그룹에 대한 거의 모든 세트를 위한 열겠다고 측정선을 &quot 실제 world"; 객체에는. 예를 들어, 그룹 또는 급지합니다 높이가 등이다. 큰 것은 충분한 설정합니까? 이를 위해 노력할 뿐 아니라, 그냥 사이의길이 급지합니다 지구상의 모든 (개인) 또는 박테리아 - 누군가 납품할 수 있다고 가정할 때 데이터!

마치 제가 잘못읽은 당초 - 어떤 같다 그냥 마드얀의 슬라이딩 창이 있어 매우 긴 대신 마드얀의 스트리밍합니다. 이 방법은 외곽진입 여전히 iqn. 처음 N 의 값을 스트리밍합니다 읽어들입니다 처음 창 관심용 대한 가치를 동시에 N + 1th 스트리밍합니다 증분할 데크레멘팅 .해당 용지함 bin 해당하는 0th 스트리밍합니다 값입니다. 이 경우 유지할 수 있도록 필요한 것은 최근 N 값이 감소, 효율적으로 수행할 수 있는 dna 의 크기 때문에 주기적으로 주소지정에 배열입니다 포지셔닝하십시오 마드얀의 스위치에서만 변경해야 할 수 있는 - 2 - 1.0,1.2 슬라이딩 창을 통해 각 단계에서 필요한 모든 장치함 요약하자면, t, 최대 it isn& # 39 마드얀의 각 단계에서 그냥 &quot 조정하십시오 마드얀의 pointer"; 면을 따라 %hsmb 장치함 수정되었습니다. 예를 들어, 새 값, 현재 그 이하로 떨어질 모두 제거되는 마드얀의 doesn& # 39, t change (오프셋된 = 0). 이 때 너무 쉽게 수용할 수 있는 방법을 ᄂ씨 메모리에 분석됩니다.

해설 (0)

필요로 하는 것은 마드얀의 자바드프리오르티크루 에서 실행 중인 친구. O (log N), O (1) O (N), 그리고 현재 마드얀의 삽입하십시오 제거하시겠습니까. 이것보다 더 많이 알고 있는 경우 분배란 데이터를 효율적으로 수행할 수 있습니다.


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

  public void insert(Integer n) {
      if (lower.isEmpty() && higher.isEmpty())
          lower.add(n);
      else {
          if (n 
해설 (4)

시점의 함수로 값을 참조할 수 있는 기능이 있는 경우, 대체를 사용하여 값을 샘플링하고 bootstrapping을 적용하여 신뢰 구간 내에서 부트스트랩된 중앙값을 생성할 수 있습니다. 이렇게 하면 들어오는 값을 데이터 구조로 계속 정렬하는 것보다 더 효율적으로 근사 중앙값을 계산할 수 있습니다.

해설 (0)

한 때 사용할 수 있는 정확한 출력입니다 슬라이드에서는 중요하지 않은 (전시 목적으로 etc.) 또한 뉴발루 토탈카운트 및 라스트메디안 합니다.

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

아주 정확한 결과를 얻을 수 등을 위한 page_display_time.

규칙: 입력 스트림 주문하십시오 준대칭 필요가 있는 페이지 표시 시간, 큰 카운트입니다 (> 30 등) 을 갖고 마드얀의 비사양 제로.

예: 페이지 부하 800 개, 평균 90ms, 실제 11ms 마드얀의 10ms.3000ms, 시간,

일반적으로 &lt 얻고, 후 30 투입물, 오류: = 20% (9ms.12ms), 그리고 점점 덜 받는다. 이후 800 투입물, 오류: +- 2% 였다.

다른 비슷한 솔루션이 바로 여기에 있는 사상가. # 15150968 https://stackoverflow.com/questions/11482529/median-filter-super-efficient-implementation/15150968

해설 (0)

다음은 jave 구축상의

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 rightMinSet;
    public Set leftMaxSet;
    public int numOfElements;

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

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

        Iterator iterMax = leftMaxSet.iterator();
        Iterator 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 {
        @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
    }
}
해설 (1)

평활화된 평균이 필요한 경우 빠르고 쉬운 방법은 최신 값에 x를 곱하고 평균 값에 (1-x)를 곱한 다음 이를 더하는 것입니다. 그러면 이것이 새로운 평균이 됩니다.

편집: 사용자가 요청한 것과는 다르며 통계적으로 유효하지는 않지만 많은 용도로 사용하기에 충분합니다.
검색을 위해 (반대표에도 불구하고) 여기에 남겨두겠습니다!

해설 (3)