Algoritmo della mediana rotante in C


114

Attualmente sto lavorando a un algoritmo per implementare un filtro mediano mobile (analogo a un filtro medio mobile) in C. Dalla mia ricerca in letteratura, sembrano esserci due modi ragionevolmente efficienti per farlo. Il primo è ordinare la finestra iniziale di valori, quindi eseguire una ricerca binaria per inserire il nuovo valore e rimuovere quello esistente ad ogni iterazione.

Il secondo (da Hardle e Steiger, 1995, JRSS-C, Algorithm 296) costruisce una struttura heap a doppia estremità, con un maxheap su un'estremità, un minheap sull'altro e la mediana al centro. Ciò produce un algoritmo in tempo lineare invece di uno che è O (n log n).

Ecco il mio problema: implementare il primo è fattibile, ma ho bisogno di eseguirlo su milioni di serie temporali, quindi l'efficienza conta molto. Quest'ultimo si sta rivelando molto difficile da implementare. Ho trovato il codice nel file Trunmed.c del codice per il pacchetto delle statistiche di R, ma è piuttosto indecifrabile.

Qualcuno sa di un'implementazione C ben scritta per l'algoritmo mediano di rotazione temporale lineare?

Modifica: collegamento al codice 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


Ho appena implementato una media mobile ... lo spostamento della mediana è un po 'più complicato. Prova a cercare su Google la mediana mobile.
Matt

Ho provato a cercare con google e codice google. Ha scoperto il codice Trunmed.c e un'implementazione in un altro linguaggio per un port SGI del codice Trunmed (da quello che ho potuto dire). Inoltre, l'algoritmo JRSS che ho citato è apparentemente l'unico nella serie della rivista per il quale il codice originale non è stato archiviato.
AWB

Quanti numeri hai in ogni serie temporale? Anche con un milione di loro, se hai solo poche migliaia di numeri, potrebbe non essere necessario più di un minuto o due per l'esecuzione (se il tuo codice è scritto in modo efficiente).
Dana the Sane

16
come è lineare la soluzione dei due cumuli? è O (n log k) dove k è la dimensione della finestra perché l'eliminazione dell'heap è O (log k).
yairchu

3
Alcune implementazioni e confronti: github.com/suomela/median-filter
Jukka Suomela

Risposte:


28

Ho guardato le R src/library/stats/src/Trunmed.calcune volte perché volevo qualcosa di simile anche in una subroutine C ++ class / C standalone. Nota che queste sono in realtà due implementazioni in una, vedi src/library/stats/man/runmed.Rd(l'origine del file della guida) che dice

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

Sarebbe bello vederlo riutilizzato in modo più autonomo. Fai volontariato? Posso aiutare con alcuni dei bit R.

Modifica 1 : oltre al collegamento alla versione precedente di Trunmed.c sopra, qui ci sono le copie SVN correnti di

Modifica 2 : Ryan Tibshirani ha del codice C e Fortran sul binning mediano veloce che può essere un punto di partenza adatto per un approccio con finestre.


Grazie Dirk. Una volta ottenuta una soluzione pulita, ho intenzione di rilasciarla sotto GPL. Sarei interessato anche alla configurazione di interfacce R e Python.
AWB

9
@ AWB Cosa è successo a questa idea? Hai incorporato la tua soluzione in un pacchetto?
Xu Wang

20

Non sono riuscito a trovare un'implementazione moderna di una struttura dati c ++ con statistica degli ordini, quindi ho finito per implementare entrambe le idee nel collegamento dei migliori programmatori suggerito da MAK ( Match Editorial : scorri verso il basso fino a FloatingMedian).

Due multiset

La prima idea partiziona i dati in due strutture di dati (heap, multiset ecc.) Con O (ln N) per inserimento / eliminazione non consente di modificare dinamicamente il quantile senza un grande costo. Cioè possiamo avere una mediana rotolante, o un 75% rotolante, ma non entrambe allo stesso tempo.

Albero dei segmenti

La seconda idea utilizza un albero dei segmenti che è O (ln N) per inserimenti / cancellazioni / query ma è più flessibile. La migliore di tutte la "N" è la dimensione del tuo intervallo di dati. Quindi, se la tua mediana mobile ha una finestra di un milione di elementi, ma i tuoi dati variano da 1..65536, allora sono necessarie solo 16 operazioni per movimento della finestra mobile di 1 milione !!

Il codice c ++ è simile a quanto pubblicato sopra da Denis ("Ecco un semplice algoritmo per i dati quantizzati")

Alberi delle statistiche dell'ordine GNU

Appena prima di arrendermi, ho scoperto che stdlibc ++ contiene alberi di statistiche dell'ordine !!!

Questi hanno due operazioni critiche:

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

Vedi il manuale di libstdc ++ policy_based_data_structures_test (cerca "split and join").

Ho inserito l'albero per l'uso in un'intestazione di convenienza per i compilatori che supportano i typedef parziali in stile 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

In realtà, i contenitori dell'estensione libstdc ++ non consentono più valori! Per impostazione predefinita ! Come suggerito dal mio nome sopra (t_order_statistic_set), più valori vengono uniti. Quindi, hanno bisogno di un po 'più di lavoro per i nostri scopi :-(
Leo Goodstadt,

Dobbiamo 1) creare una mappa dei valori da contare (invece dei set) 2) le dimensioni dei rami dovrebbero riflettere il conteggio delle chiavi (libstdc ++ - v3 / include / ext / pb_ds / detail / tree_policy / order_statistics_imp.hpp) ereditate da l'albero e 3) sovraccarico insert () per aumentare il conteggio / chiamare update_to_top () se il valore è già presente 4) sovraccarico erase () per diminuire il conteggio / chiamare update_to_top () se il valore non è univoco (vedere libstdc ++ - v3 / include / ext / pb_ds / detail / rb_tree_map_ / rb_tree_.hpp) Eventuali volontari ??
Leo Goodstadt,

15

Ho eseguito un'implementazione in C qui . Alcuni dettagli in più sono in questa domanda: Mediana mobile nell'implementazione di C - Turlach .

Utilizzo del campione:

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
Ottima, veloce e chiara implementazione basata su heap min-median-max. Ottimo lavoro.
Johannes Rudolph

Come posso trovare la versione Java di questa soluzione?
Hengameh

10

Uso questo stimatore mediano incrementale:

median += eta * sgn(sample - median)

che ha la stessa forma del più comune stimatore medio:

mean += eta * (sample - mean)

Qui eta è un piccolo parametro della velocità di apprendimento (ad esempio 0.001), ed sgn()è la funzione signum che restituisce uno di {-1, 0, 1}. (Usa una costante etacome questa se i dati non sono stazionari e vuoi tenere traccia dei cambiamenti nel tempo; altrimenti, per sorgenti stazionarie usa qualcosa di simile eta = 1 / na convergere, dov'è nil numero di campioni visti finora.)

Inoltre, ho modificato lo stimatore mediano per farlo funzionare per quantili arbitrari. In generale, una funzione quantile ti dice il valore che divide i dati in due frazioni: pe 1 - p. Quanto segue stima questo valore in modo incrementale:

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

Il valore pdovrebbe essere compreso tra [0, 1]. Ciò essenzialmente sposta l' sgn()output simmetrico della funzione {-1, 0, 1}in modo che si inclini verso un lato, suddividendo i campioni di dati in due contenitori di dimensioni non uguali (le frazioni pe 1 - pdei dati sono rispettivamente inferiori / maggiori della stima quantile). Si noti che per p = 0.5, questo si riduce allo stimatore mediano.


2
Fantastico, ecco una modifica che regola 'eta' in base alla media corrente ... (la media è usata come stima approssimativa della mediana in modo che converga su valori grandi alla stessa velocità con cui converge su valori piccoli). cioè eta viene sintonizzato automaticamente. stackoverflow.com/questions/11482529/…
Jeff McClintock

3
Per una tecnica simile, vedere questo documento sullo streaming frugale: arxiv.org/pdf/1407.1121v1.pdf Può stimare qualsiasi quartile e si adatta ai cambiamenti nella media. È necessario memorizzare solo due valori: ultima stima e direzione dell'ultima regolazione (+1 o -1). L'algoritmo è semplice da implementare. Trovo che l'errore sia entro il 5% circa il 97% delle volte.
Paul Chernoch

9

Ecco un semplice algoritmo per i dati quantizzati (mesi dopo):

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

La mediana mobile può essere trovata mantenendo due partizioni di numeri.

Per mantenere le partizioni utilizzare Min Heap e Max Heap.

Max Heap conterrà numeri minori di uguale alla mediana.

Heap minimo conterrà numeri maggiori di uguale alla mediana.

Vincolo di bilanciamento: se il numero totale di elementi è pari, entrambi gli heap dovrebbero avere elementi uguali.

se il numero totale di elementi è dispari, Max Heap avrà un elemento in più rispetto a Min Heap.

Elemento mediano: se entrambe le partizioni hanno lo stesso numero di elementi, la mediana sarà la metà della somma dell'elemento massimo dalla prima partizione e dell'elemento minimo dalla seconda partizione.

Altrimenti la mediana sarà l'elemento max dalla prima partizione.

Algoritmo-
1- Prendi due Heap (1 Heap Min e 1 Heap Max)
   Max Heap conterrà la prima metà del numero di elementi
   Min Heap conterrà la seconda metà del numero di elementi

2- Confronta il nuovo numero dallo stream con la parte superiore di Max Heap, 
   se è minore o uguale, aggiungi quel numero nell'heap massimo. 
   Altrimenti aggiungi il numero in Min Heap.

3- se min Heap ha più elementi di Max Heap 
   quindi rimuovere l'elemento superiore di Min Heap e aggiungere Max Heap.
   se max Heap ha più di un elemento rispetto a Min Heap 
   quindi rimuovere l'elemento superiore di Max Heap e aggiungere Min Heap.

4- Se Entrambi gli heap hanno lo stesso numero di elementi, allora
   mediana sarà la metà della somma di max element da Max Heap e min element da Min Heap.
   Altrimenti la mediana sarà l'elemento max dalla prima partizione.
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();
    }
}

Non mi è chiaro quale vantaggio offra una terza risposta Java per una domanda C. Dovresti porre una nuova domanda e quindi fornire la tua risposta Java a quella domanda.
jww

la logica è morta dopo aver letto questo "quindi rimuovere l'elemento superiore di Min Heap e aggiungere in Min Heap." Almeno abbi la cortesia di leggere l'algo prima di postare
Cyclotron3x3

4
Questo algoritmo non è per una mediana mobile ma per la mediana di un numero crescente di elementi. Per la mediana rotolante, è necessario rimuovere anche un elemento dai cumuli, che deve essere trovato per primo.
Walter

2

Forse vale la pena sottolineare che esiste un caso speciale che ha una semplice soluzione esatta: quando tutti i valori nel flusso sono numeri interi all'interno di un intervallo definito (relativamente) piccolo. Ad esempio, si supponga che debbano essere tutti compresi tra 0 e 1023. In questo caso è sufficiente definire un array di 1024 elementi e un conteggio e cancellare tutti questi valori. Per ogni valore nel flusso incrementare il bin e il conteggio corrispondenti. Dopo la fine dello stream, trova il contenitore che contiene il valore count / 2 più alto - facilmente ottenuto aggiungendo contenitori successivi a partire da 0. Utilizzando lo stesso metodo è possibile trovare il valore di un ordine di classificazione arbitrario. (C'è una piccola complicazione se sarà necessario rilevare la saturazione dei contenitori e "aggiornare" la dimensione degli scomparti di archiviazione a un tipo più grande durante un'esecuzione.)

Questo caso speciale può sembrare artificiale, ma in pratica è molto comune. Può anche essere applicato come approssimazione per i numeri reali se si trovano in un intervallo e si conosce un livello di precisione "abbastanza buono". Ciò varrebbe praticamente per qualsiasi serie di misurazioni su un gruppo di oggetti del "mondo reale". Ad esempio, l'altezza o il peso di un gruppo di persone. Non è un set abbastanza grande? Funzionerebbe altrettanto bene per la lunghezza o il peso di tutti i batteri (individuali) del pianeta, supponendo che qualcuno possa fornire i dati!

Sembra che abbia letto male l'originale, il che sembra che voglia una mediana della finestra scorrevole invece della mediana di un flusso molto lungo. Questo approccio funziona ancora per quello. Carica i primi N valori di flusso per la finestra iniziale, quindi per il valore di flusso N + 1 ° incrementa il contenitore corrispondente mentre decrementa il contenitore corrispondente allo 0 ° valore di flusso. È necessario in questo caso mantenere gli ultimi N valori per consentire il decremento, che può essere effettuato in modo efficiente indirizzando ciclicamente un array di dimensione N. Poiché la posizione della mediana può cambiare solo di -2, -1,0,1 , 2 su ogni gradino della finestra scorrevole, non è necessario sommare tutti i contenitori fino alla mediana su ogni gradino, basta regolare il "puntatore mediano" a seconda di quale lato / i contenitori sono stati modificati. Per esempio, se sia il nuovo valore che quello rimosso scendono al di sotto della mediana corrente allora non cambia (offset = 0). Il metodo si interrompe quando N diventa troppo grande per essere tenuto comodamente in memoria.


1

Se si è in grado di fare riferimento ai valori in funzione dei punti nel tempo, è possibile campionare i valori con la sostituzione, applicando il bootstrap per generare un valore mediano bootstrap all'interno degli intervalli di confidenza. Ciò potrebbe consentire di calcolare una mediana approssimativa con maggiore efficienza rispetto all'ordinamento costante dei valori in entrata in una struttura di dati.


1

Per coloro che hanno bisogno di una mediana in esecuzione in Java ... PriorityQueue è tuo amico. O (log N) inserire, O (1) mediana corrente e O (N) rimuovere. Se conosci la distribuzione dei tuoi dati puoi fare molto meglio di così.

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 ++ ha alberi delle statistiche degli ordini da gnu in un'estensione della libreria standard. Vedi il mio post qui sotto.
Leo Goodstadt,

Penso che il tuo codice non sia stato inserito correttamente qui. Ci sono alcune parti incomplete come: }), higher = new PriorityQueue<Integer>();o new PriorityQueue<Integer>(10,. Non sono riuscito a eseguire il codice.
Hengameh

@Hengameh Java termina le istruzioni con punto e virgola: le interruzioni di riga non hanno alcuna importanza. Devi averlo copiato in modo errato.
Matthew Read il

Dovresti porre una nuova domanda e quindi fornire la tua risposta Java a quella domanda.
jww

0

Eccone uno che può essere utilizzato quando l'output esatto non è importante (per scopi di visualizzazione, ecc.) È necessario totalcount e lastmedian, più il newvalue.

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

Produce risultati abbastanza esatti per cose come page_display_time.

Regole: il flusso di input deve essere regolare nell'ordine del tempo di visualizzazione della pagina, avere un conteggio elevato (> 30 ecc.) E avere una mediana diversa da zero.

Esempio: tempo di caricamento della pagina, 800 elementi, 10 ms ... 3000 ms, media 90 ms, mediana reale: 11 ms

Dopo 30 input, l'errore mediano è generalmente <= 20% (9ms..12ms) e diventa sempre meno. Dopo 800 input, l'errore è + -2%.

Un altro pensatore con una soluzione simile è qui: Filtro mediano Implementazione super efficiente


-1

Ecco l'implementazione 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
    }
}

Dovresti porre una nuova domanda e quindi fornire la tua risposta Java a quella domanda.
jww

-4

Se hai solo bisogno di una media livellata, un modo semplice / veloce è moltiplicare l'ultimo valore per x e il valore medio per (1-x), quindi aggiungerli. Questa diventa quindi la nuova media.

modifica: non è quello che l'utente ha chiesto e non è statisticamente valido ma abbastanza buono per molti usi.
Lo lascio qui (nonostante i voti negativi) per la ricerca!


2
Questo calcola la media. Vuole la mediana. Inoltre, sta calcolando la mediana di una finestra scorrevole di valori, non dell'intero set.
A. Levy

1
Questo calcola una media corrente di una finestra di valori con una costante di decadimento che dipende da X: è molto utile dove le prestazioni contano e non puoi essere disturbato a fare un filtro kalman. L'ho inserito in modo che la ricerca possa trovarlo.
Martin Beckett,

Questo è ciò a cui ho anche pensato immediatamente, avendo implementato un tale filtro come un filtro passa-basso molto semplice ed economico per un'app audio.
James Morris,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.