C'de yuvarlanan medyan algoritması


114

Şu anda C'de bir yuvarlanan medyan filtresi (yuvarlanan ortalama filtresine benzer) uygulamak için bir algoritma üzerinde çalışıyorum. Literatür araştırmamdan, bunu yapmanın makul derecede etkili iki yolu var gibi görünüyor. Birincisi, değerlerin ilk penceresini sıralamak, ardından yeni değeri eklemek ve her yinelemede mevcut olanı kaldırmak için bir ikili arama yapmaktır.

İkincisi (Hardle ve Steiger, 1995, JRSS-C, Algorithm 296'dan), bir ucunda bir maxheap, diğerinde bir minheap ve ortada bir medyan olan çift uçlu bir yığın yapısı oluşturur. Bu, O (n log n) olan bir algoritma yerine lineer zamanlı bir algoritma verir.

Benim sorunum şu: ilkini uygulamak mümkün, ancak bunu milyonlarca zaman serisinde çalıştırmam gerekiyor, bu nedenle verimlilik çok önemli. İkincisinin uygulanması çok zor. R istatistik paketinin kodunun Trunmed.c dosyasında kod buldum, ancak bu kod çözülemez.

Doğrusal zaman yuvarlanan medyan algoritması için iyi yazılmış bir C uygulaması bilen var mı?

Düzenleme: Trunmed.c koduna bağlantı http://google.com/codesearch/p?hl=tr&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c


Sadece hareketli bir ortalama uygulandı ... hareketli medyan biraz daha karmaşık. Googling hareketli medyanı deneyin.
Matt

Google ve google kod araması denendi. Trunmed.c kodunu ve Trunmed kodunun bir SGI portu için başka bir dilde bir uygulama (söyleyebildiklerimden) ortaya çıktı. Ayrıca, alıntı yaptığım JRSS algoritması, görünüşe göre derginin serisinde orijinal kodun arşivlenmediği tek algoritmadır.
AWB

Her zaman serisinde kaç tane sayınız var? Bir milyon bile olsa, yalnızca birkaç bin numaranız varsa, çalıştırılması bir veya iki dakikadan uzun sürmeyebilir (kodunuz verimli bir şekilde yazıldıysa).
Dana the Sane

16
iki yığın çözümü nasıl doğrusaldır? O (n log k) burada k pencere boyutudur, çünkü yığının silinmesi O (log k) 'dir.
yairchu

3
Bazı uygulamalar ve karşılaştırmalar: github.com/suomela/median-filter
Jukka Suomela

Yanıtlar:


28

R'ye baktım src/library/stats/src/Trunmed.cBağımsız bir C ++ sınıfı / C alt yordamında da benzer bir şey istediğim için R'lere birkaç kez . Bunun aslında bir arada iki uygulama olduğunu unutmayın, bkz. src/library/stats/man/runmed.Rd(Yardım dosyasının kaynağı)

\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}.}
  }
}

Bunun daha bağımsız bir şekilde yeniden kullanıldığını görmek güzel olurdu. Gönüllü müsün? Bazı R bitlerinde yardımcı olabilirim.

Düzenle 1 : Yukarıdaki Trunmed.c'nin eski sürümüne olan bağlantının yanı sıra, işte güncel SVN kopyaları

Düzenleme 2 : Ryan Tibshirani, pencereli bir yaklaşım için uygun bir başlangıç ​​noktası olabilecek hızlı medyan gruplamada bazı C ve Fortran kodlarına sahiptir .


Teşekkürler Dirk. Temiz bir çözüm elde ettiğimde, onu GPL altında yayınlamayı planlıyorum. Bir R ve Python arayüzleri kurmakla da ilgilenirim.
AWB

9
@AWB Bu fikre ne oldu? Çözümünüzü bir pakete dahil ettiniz mi?
Xu Wang

20

Sipariş istatistiği ile bir c ++ veri yapısının modern bir uygulamasını bulamadım, bu yüzden MAK tarafından önerilen en iyi kodlayıcılar bağlantısında her iki fikri de uyguladım ( Match Editorial : FloatingMedian'a gidin).

İki çoklu set

İlk fikir, verileri ekleme / silme başına O (ln N) ile iki veri yapısına (yığınlar, çoklu kümeler, vb.) Böler, niceliğin büyük bir maliyet olmadan dinamik olarak değiştirilmesine izin vermez. Yani bir yuvarlanan medyan veya% 75 yuvarlanan bir medyaya sahip olabiliriz, ancak ikisi aynı anda olamaz.

Segment ağacı

İkinci fikir, ekleme / silme / sorgular için O (ln N) olan ancak daha esnek olan bir bölüm ağacı kullanır. Tüm "N" değerlerinin en iyisi, veri aralığınızın boyutudur. Dolayısıyla, hareketli medyanınız bir milyon öğeden oluşan bir pencereye sahipse, ancak verileriniz 1.65536'dan değişiyorsa, 1 milyonluk kayan pencerenin hareketi başına yalnızca 16 işlem gerekir !!

C ++ kodu, Denis'in yukarıda yazdıklarına benzer ("İşte nicelleştirilmiş veriler için basit bir algoritma")

GNU Sıralaması İstatistik Ağaçları

Vazgeçmeden hemen önce, stdlibc ++ 'nın sıra istatistik ağaçları içerdiğini öğrendim !!!

Bunların iki kritik işlemi vardır:

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

Libstdc ++ manuel policy_based_data_structures_test'e bakın ("böl ve birleştir" için arama yapın).

Ağacı, c ++ 0x / c ++ 11 tarzı kısmi yazı tiplerini destekleyen derleyiciler için uygun bir üstbilgide kullanmak üzere sardım:

#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

Aslında, libstdc ++ uzatma konteynerleri yok değil tasarımla! Birden çok değer için izin! Yukarıdaki adımın önerdiği gibi (t_order_statistic_set), birden çok değer birleştirildi. Dolayısıyla, bizim amaçlarımız için biraz daha çalışmaya ihtiyaçları var :-(
Leo Goodstadt

1) sayılacak değerlerin eşlemesini yapmalıyız (kümeler yerine) 2) dal büyüklükleri anahtarların sayısını yansıtmalıdır (libstdc ++ - v3 / include / ext / pb_ds / detail / tree_policy / order_statistics_imp.hpp) ağaç ve 3) aşırı yükleme insert () sayımı artırmak için update_to_top () değer zaten mevcutsa 4) sayma / call update_to_top () değerini azaltmak için aşırı yük silme () değer benzersiz değilse (Bkz. libstdc ++ - v3 / include / ext / pb_ds / detail / rb_tree_map_ / rb_tree_.hpp) Herhangi bir gönüllü?
Leo Goodstadt

15

Burada bir C uygulaması yaptım . Bu soruda birkaç ayrıntı daha var: C - Turlach uygulamasında yuvarlanan medyan .

Örnek kullanım:

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-medyan-maks yığınına dayalı harika, hızlı ve net uygulama. Çok iyi iş.
Johannes Rudolph

Bu çözümün Java sürümünü nasıl bulabilirim?
Hengameh

10

Bu artımlı medyan tahmin ediciyi kullanıyorum:

median += eta * sgn(sample - median)

daha yaygın ortalama tahmin ediciyle aynı forma sahip:

mean += eta * (sample - mean)

İşte eta küçük bir öğrenme hızı parametresidir (örn. 0.001) Ve sgn()bunlardan birini döndüren işaret fonksiyonudur {-1, 0, 1}. ( etaVeriler sabit değilse ve zaman içindeki değişiklikleri izlemek istiyorsanız bunun gibi bir sabit kullanın ; aksi takdirde, durağan kaynaklar eta = 1 / niçin yakınsama gibi bir şey kullanın , nşu ana kadar görülen örnek sayısı nerede ?)

Ayrıca, medyan tahmin ediciyi rastgele nicelikler için çalışmasını sağlayacak şekilde değiştirdim. Genel olarak bir nicelik işlevi size verileri iki kesire bölen değeri söyler: pve 1 - p. Aşağıdaki, bu değeri aşamalı olarak tahmin etmektedir:

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

Değer piçinde olmalıdır [0, 1]. Bu, temelde sgn()işlevin simetrik çıktısını {-1, 0, 1}bir tarafa doğru eğilecek şekilde kaydırır, veri örneklerini eşit olmayan boyutlu iki bölmeye böler (kesirler pve 1 - pverilerin sırasıyla nicelik tahmininden küçük / büyüktür). İçin unutmayınp = 0.5 , bu medyan tahminleyicisini azaltır.


2
Harika, İşte 'eta'yı çalışan ortalamaya göre ayarlayan bir değişiklik ... (ortalama, medyanın kaba bir tahmini olarak kullanılır, böylece küçük değerlerde yakınsadığı aynı oranda büyük değerlere yakınlaşır). yani eta otomatik olarak ayarlanır. stackoverflow.com/questions/11482529/…
Jeff McClintock

3
Benzer bir teknik için, tutumlu akışla ilgili şu makaleye bakın: arxiv.org/pdf/1407.1121v1.pdf Herhangi bir çeyreği tahmin edebilir ve ortalamadaki değişikliklere uyum sağlayabilir. Yalnızca iki değer kaydetmenizi gerektirir: son tahmin ve son ayarlamanın yönü (+1 veya -1). Algoritmanın uygulanması basittir. Hatanın zamanın% 5'i yaklaşık% 97'si dahilinde olduğunu görüyorum.
Paul Chernoch

9

İşte nicelleştirilmiş veriler için basit bir algoritma (aylar sonra):

""" 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

Yuvarlanan medyan, iki sayı bölümü korunarak bulunabilir.

Bölümleri korumak için Min Heap ve Max Heap kullanın.

Maksimum Yığın, medyandan daha küçük sayılar içerecektir.

Min Yığın, medyandan daha büyük sayılar içerecektir.

Dengeleme Kısıtlaması: Toplam eleman sayısı çift ise, o zaman her iki öbek de eşit elemanlara sahip olmalıdır.

toplam eleman sayısı tek ise, Max Heap'in Min Heap'ten bir fazla elemanı olacaktır.

Medyan Eleman: Her iki bölümün de eşit sayıda elemanı varsa, o zaman medyan, birinci bölümden maksimum eleman ve ikinci bölümden minimum eleman toplamının yarısı olacaktır.

Aksi takdirde medyan, ilk bölümden maksimum öğe olacaktır.

Algorithm-
1- İki Yığın Al (1 Min Yığın ve 1 Maks Yığın)
   Maksimum Yığın ilk yarı sayıda eleman içerecektir
   Min Yığın, ikinci yarı sayıda eleman içerecektir

2- Akıştaki yeni sayıyı Max Heap'in üst kısmı ile karşılaştırın, 
   daha küçük veya eşitse, bu sayıyı maks. yığın içine ekleyin. 
   Aksi takdirde Min Heap'e sayı ekleyin.

3- Min Heap'in Max Heap'ten daha fazla elemanı varsa 
   ardından Min Heap'in üst elemanını kaldırın ve Max Heap'e ekleyin.
   Max Heap, Min Heap'de olduğundan daha fazla elemente sahipse 
   sonra Max Heap'in üst elemanını kaldırın ve Min Heap'e ekleyin.

4- Her iki yığında da eşit sayıda eleman varsa
   medyan, Max Heap'ten maksimum elementin ve Min Heap'ten minimum elementin toplamının yarısı olacaktır.
   Aksi takdirde medyan, ilk bölümden maksimum öğe olacaktır.
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();
    }
}

Üçüncü bir Java cevabının bir C sorusu için ne kadar fayda sağladığı bana açık değil. Yeni bir soru sormalı ve ardından bu soruya Java cevabınızı vermelisiniz.
jww

Bunu okuduktan sonra mantık öldü 'ardından Min Heap'in en üst öğesini kaldırın ve Min Heap ekleyin.' En azından yayınlamadan önce algo'yu okumak için nezaket gösterin
Cyclotron3x3

4
Bu algoritma, dönen bir medyan için değil, artan sayıda öğenin medyanı içindir. Yuvarlanan medyan için, ilk önce bulunması gereken bir öğeyi de yığınlardan çıkarmak gerekir.
Walter

2

Basit ve kesin bir çözüme sahip özel bir durum olduğuna işaret etmek faydalı olabilir: akıştaki tüm değerler (nispeten) küçük bir tanımlanmış aralık içinde tamsayılar olduğunda. Örneğin, hepsinin 0 ile 1023 arasında olması gerektiğini varsayın. Bu durumda sadece 1024 öğeden oluşan bir dizi ve bir sayı tanımlayın ve bu değerlerin tümünü silin. Akıştaki her değer için karşılık gelen bölmeyi ve sayımı artırın. Akış sona erdikten sonra, sayı / 2 en yüksek değeri içeren bölmeyi bulun - 0'dan başlayarak art arda gelen bölmeleri ekleyerek kolayca gerçekleştirilebilir. Aynı yöntemi kullanarak, rastgele bir sıra sırasının değeri bulunabilir. (Bölme doygunluğunu tespit etmek ve çalışma sırasında depolama bölmelerinin boyutunu daha büyük bir türe "yükseltmek" gerekirse küçük bir karmaşıklık vardır.)

Bu özel durum yapay görünebilir, ancak pratikte çok yaygındır. Ayrıca, bir aralık içinde yer almaları ve "yeterince iyi" bir hassasiyet seviyesi bilinmesi durumunda, gerçek sayılar için bir yaklaşım olarak da uygulanabilir. Bu, bir grup "gerçek dünya" nesnesi üzerindeki hemen hemen tüm ölçümler için geçerli olacaktır. Örneğin, bir grup insanın boyları veya ağırlıkları. Yeterince büyük bir set değil mi? Gezegendeki tüm (tek tek) bakterilerin uzunlukları veya ağırlıkları için de işe yarar - birilerinin veriyi sağlayabileceğini varsayarsak!

Görünüşe göre orijinali yanlış okumuşum - bu, çok uzun bir akışın sadece medyanı yerine kayan bir pencere medyanı istiyor gibi görünüyor. Bu yaklaşım hala bunun için çalışıyor. İlk pencere için ilk N akış değerini yükleyin, ardından N + 1'inci akış değeri için karşılık gelen bölmeyi artırın ve 0'ıncı akış değerine karşılık gelen bölmeyi azaltın. Bu durumda, azaltmaya izin vermek için son N değerlerinin tutulması gerekir; bu, N boyutundaki bir diziyi döngüsel olarak adresleyerek verimli bir şekilde yapılabilir. Medyan konumu yalnızca -2, -1,0,1 kadar değişebilir. , 2, kayan pencerenin her adımında, tüm bölmeleri her adımda medyana kadar toplamak gerekli değildir, yalnızca bölmelerin değiştirildiği taraf (lar) a bağlı olarak "medyan işaretçisini" ayarlayın. Örneğin, hem yeni değer hem de kaldırılan mevcut medyanın altına düşerse, o zaman değişmez (ofset = 0). Yöntem, N bellekte rahatça tutulamayacak kadar büyüdüğünde bozulur.


1

Zamanında noktalarının bir fonksiyonu olarak referans değerleri yeteneği varsa, uygulayarak değiştirme ile değerleri örnek bu işe koşulması güven aralıkları içinde bootstrapped medyan değer oluşturmak için. Bu, gelen değerleri sürekli olarak bir veri yapısında sıralamaktan daha verimli bir yaklaşık medyan hesaplamanıza izin verebilir.


1

Java'da medyana ihtiyaç duyanlar için ... PriorityQueue sizin dostunuzdur. O (log N) ekle, O (1) mevcut medyan ve O (N) kaldır. Verilerinizin dağıtımını biliyorsanız, bundan çok daha iyisini yapabilirsiniz.

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 ++, standart kitaplığın bir uzantısında gnu'dan istatistik ağaçları sırasına sahiptir. Aşağıdaki yazıma bakın.
Leo Goodstadt

Kodunuzun buraya doğru yerleştirilmediğini düşünüyorum. Orada gibi bazı eksik kısımlar var: }), higher = new PriorityQueue<Integer>();veya new PriorityQueue<Integer>(10,. Kodu çalıştıramadım.
Hengameh

@Hengameh Java, ifadeleri noktalı virgülle sonlandırır - satır sonlarının önemi yoktur. Yanlış kopyalamış olmalısın.
Matthew

Yeni bir soru sormalı ve ardından bu soruya Java cevabınızı vermelisiniz.
jww

0

İşte tam çıktının önemli olmadığı durumlarda kullanılabilecek (görüntüleme amaçlı vb.) Totalcount ve lastmedian ile yeni değere ihtiyacınız var.

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

Page_display_time gibi şeyler için oldukça kesin sonuçlar üretir.

Kurallar: Giriş akışının sayfa görüntüleme süresine göre düzgün, sayısının büyük olması (> 30 vb.) Ve sıfır olmayan bir medyana sahip olması gerekir.

Örnek: sayfa yükleme süresi, 800 öğe, 10ms ... 3000ms, ortalama 90ms, gerçek medyan: 11ms

30 girişten sonra, medyan hatası genellikle <=% 20'dir (9ms..12ms) ve gittikçe daha az olur. 800 girişten sonra hata +% 2'dir.

Benzer bir çözüme sahip başka bir düşünür burada: Medyan Filtresi Süper verimli uygulama


-1

İşte java uygulaması

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
    }
}

Yeni bir soru sormalı ve ardından bu soruya Java cevabınızı vermelisiniz.
jww

-4

Düzgünleştirilmiş bir ortalamaya ihtiyacınız varsa, hızlı / kolay bir yol, en son değeri x ve ortalama değeri (1-x) ile çarpmaktır, sonra bunları ekleyin. Bu daha sonra yeni ortalama olur.

edit: Kullanıcının istediği şey değil ve istatistiksel olarak geçerli değil ama birçok kullanım için yeterince iyi.
Arama için (olumsuz oylara rağmen) burada bırakacağım!


2
Bu ortalamayı hesaplar. Medyanı istiyor. Ayrıca, tüm kümenin değil, kayan bir değerler penceresinin medyanını hesaplıyor.
A. Levy

1
Bu, X'e bağlı olarak bozulma sabiti olan bir değerler penceresinin hareketli ortalamasını hesaplar - performansın önemli olduğu yerlerde çok kullanışlıdır ve bir kalman filtresi yapmaktan rahatsız olmazsınız. Onu aramanın bulabilmesi için koydum.
Martin Beckett

Bir ses uygulaması için çok basit ve ucuz bir düşük geçiş filtresi gibi bir filtre uyguladığım için hemen düşündüğüm şey buydu.
James Morris
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.