저는 현재 롤링 중앙값 필터(롤링 평균 필터와 유사)를 구현하는 알고리즘을 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
독립 실행형 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 <- 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 코드를 가지고 있으며, 이는 윈도우 접근 방식에 적합한 출발점이 될 수 있습니다.
아이디어를 처음 두 개의 데이터 구조 (힙, 중복집합 등) 로 데이터를 파티션당 함께 O (log N) 로 변경할 수 없는 큰 비용 없이 무중단으로 분위수 삽입하십시오 / 삭제. I. e. 둘 수 있습니다, 또는 롤링 (rolling 마드얀의 75% 동시에건간에.
O (log N) 의 두 번째 idea 는 세그먼트마다 진단트리는 원하는거요 삽입 / 삭제 / 쿼리합니다 하지만 더 유연하다. 무엇보다도 이 " N"; 데이터의 크기가 범위. 그래서 데이터의 경우 a 는 롤링 마드얀의 접할 수 있지만, 고작 16 만개 품목을 1.65536 다릅니다 운동 후 당 1 만 창이 운영체 필요합니다!!
C++ 코드는 어떤 데니스 게시하기를 비슷한 위 (# 39 에 대한 간단한 알고리즘입니다 양자화 data" Here& ";)
전에 내가 그냥 포기하는 것으로, 통계 주문하십시오 stdlibc++ map_layer 진단트리!!!
이러한 두 가지 중요한 작업:
iter = tree.find_by_order(value)
order = tree.order_of_key(value)
참조 [libstdc++ 수작업식 policy_based_data_structures_test] [2] (검색하겠습니다 분할 및 join" ";).
나는 편의를 위해 관련 c++0x/c++11 랩된 나무를요 사용하기 위해 헤더입니다 컴파일러에도 스타일 부분 메리 페데프스:
#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
[1]: http://www.topcoder.com/tc, d1, d2 = = = match_editorials& srm310 모듈에서는 Static&? [2]: ++/manual/policy_based_data_structures_test.html http://gcc.gnu.org/onlinedocs/libstdc
사용 예제:
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);
}
}
내가 사용하는 이 변동분 마드얀의 예측기를:
median += eta * sgn(sample - median)
더 일반적인 디스크입니다. diskid 같은 형태의 짓궂군요 예측기를:
mean += eta * (sample - mean)
역시 내가 일할 수 있도록 예측기를 분위수 마드얀의 수정한 것이다. 일반적으로 [분위수 함수은] (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 마드얀의 예측기를.
""" 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
두 숫자의 파티션당 롤링 마드얀의 유지하여 찾을 수 있습니다.
파티션당 최대 힙 힙 및 유지에 필요한 최소 사용합니다.
최대 힙 median.* 같은 보다 작은 숫자임 포함됩니다.
최소 힙 포함될 보다 큰 숫자를 median.* 같음
밸런싱과 Constraint:* 그런데도 요소를 모두 같을 경우 총 요소는 힙 있어야 합니다.
그리고 하나 더 이상 할 경우 최대 힙 홀수입니다 총 요소는 요소 최소 힙.
그렇지 않으면 마드얀의 적립율은 맥스야 요소점 에서 첫 번째 파티션. < pre>; 알고리즘입니다 - 1 - 2 시행하십시오 힙 (1 분 최대 힙 힙 (heap) 과 1) 올 상반기 최대 힙 (heap) 에는 여러 요소를 하반기 여러 요소를 포함할 수 최소 힙
2 - 비교하십시오 숫자를 통해 새로운 스트리밍합니다 상단형 최대 힙, 같거나 작을 경우 추가 그 수를 최대 힙. 그렇지 않으면 추가 숫자를 최소 힙.
이 보다 더 많은 요소가 최대 힙 힙 (heap) 는 3 분 최대 최소 힙 힙 및 주요 요소 제거 후 추가. 만약 최대 힙 둘 이상의 요소점 비해 최소 힙 최대 최소 힙 힙 및 주요 요소 제거 후 추가.
4 -) 는 여러 요소를 모두 같을 경우 다음 힙 최대 힙 합을 통해 최대 요소 및 최소 절반이 될 마드얀의 요소점 에서 최소 힙. 그렇지 않으면 마드얀의 적립율은 맥스야 요소점 시작한 첫 파티션에만. < /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<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();
}
}
그것은 아마 가치가 있다는 것을 지적하고, 특별한 경우에는 디스크입니다. diskid 간단한 그 라그랑지언은 다음과 같다: 모든 정수 (상대적으로) 의 값을 정의할 때 작은 범위 내에서 스트리밍합니다 있다. 예를 들어, 1023년 모든 0-사이의 방황케 solaris. 한다. 이 경우 그냥 배열입니다 요소 및 횟수, 선택해제합니다 정의하십시오 1024년 이러한 값이 모두. 각 값이 증분할 스트리밍합니다 .해당 용지함 및 카운트입니다. 이후 가장 높은 값을 포함하는 count/2 motor1.1end 스트리밍합니다 용지함 찾을 수 있습니다 - 0 부터 시작하여 연속적인 장치함 추가하기만 동일한 방법으로 자의적 랭크 오더할 가치를 찾을 수 있습니다. (감지할 수 있을 경우, 채도와 " 경미함 합병증 용지함 upgrading". 크기가 큰 유형: 저장 장치함 실행 중에 필요하게 됩니다.)
이 특수한 경우가 있지만, 실제로는 매우 일반적인 인공 보일 수 있습니다. 또한 실제 적용할 수 있는 범위 내에 위치한 근사화를 표시됨과 숫자임 슬래시나 및 " 优秀 enough"; 수준의 precisiontm 알려져 있다. 이 그룹에 대한 거의 모든 세트를 위한 열겠다고 측정선을 " 실제 world"; 객체에는. 예를 들어, 그룹 또는 급지합니다 높이가 등이다. 큰 것은 충분한 설정합니까? 이를 위해 노력할 뿐 아니라, 그냥 사이의길이 급지합니다 지구상의 모든 (개인) 또는 박테리아 - 누군가 납품할 수 있다고 가정할 때 데이터!
마치 제가 잘못읽은 당초 - 어떤 같다 그냥 마드얀의 슬라이딩 창이 있어 매우 긴 대신 마드얀의 스트리밍합니다. 이 방법은 외곽진입 여전히 iqn. 처음 N 의 값을 스트리밍합니다 읽어들입니다 처음 창 관심용 대한 가치를 동시에 N + 1th 스트리밍합니다 증분할 데크레멘팅 .해당 용지함 bin 해당하는 0th 스트리밍합니다 값입니다. 이 경우 유지할 수 있도록 필요한 것은 최근 N 값이 감소, 효율적으로 수행할 수 있는 dna 의 크기 때문에 주기적으로 주소지정에 배열입니다 포지셔닝하십시오 마드얀의 스위치에서만 변경해야 할 수 있는 - 2 - 1.0,1.2 슬라이딩 창을 통해 각 단계에서 필요한 모든 장치함 요약하자면, t, 최대 it isn& # 39 마드얀의 각 단계에서 그냥 " 조정하십시오 마드얀의 pointer"; 면을 따라 %hsmb 장치함 수정되었습니다. 예를 들어, 새 값, 현재 그 이하로 떨어질 모두 제거되는 마드얀의 doesn& # 39, t change (오프셋된 = 0). 이 때 너무 쉽게 수용할 수 있는 방법을 ᄂ씨 메모리에 분석됩니다.
필요로 하는 것은 마드얀의 자바드프리오르티크루 에서 실행 중인 친구. 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();
}
}
시점의 함수로 값을 참조할 수 있는 기능이 있는 경우, 대체를 사용하여 값을 샘플링하고 bootstrapping을 적용하여 신뢰 구간 내에서 부트스트랩된 중앙값을 생성할 수 있습니다. 이렇게 하면 들어오는 값을 데이터 구조로 계속 정렬하는 것보다 더 효율적으로 근사 중앙값을 계산할 수 있습니다.
한 때 사용할 수 있는 정확한 출력입니다 슬라이드에서는 중요하지 않은 (전시 목적으로 etc.) 또한 뉴발루 토탈카운트 및 라스트메디안 합니다.
{
totalcount++;
newmedian=lastmedian+(newvalue>lastmedian?1:-1)*(lastmedian==0?newvalue: lastmedian/totalcount*2);
}
아주 정확한 결과를 얻을 수 등을 위한 page_display_time.
규칙: 입력 스트림 주문하십시오 준대칭 필요가 있는 페이지 표시 시간, 큰 카운트입니다 (> 30 등) 을 갖고 마드얀의 비사양 제로.
예: 페이지 부하 800 개, 평균 90ms, 실제 11ms 마드얀의 10ms.3000ms, 시간,
일반적으로 < 얻고, 후 30 투입물, 오류: = 20% (9ms.12ms), 그리고 점점 덜 받는다. 이후 800 투입물, 오류: +- 2% 였다.
다른 비슷한 솔루션이 바로 여기에 있는 사상가. # 15150968 https://stackoverflow.com/questions/11482529/median-filter-super-efficient-implementation/15150968
다음은 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<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
}
}