2010-09-25 9 views
16

Ich habe eine Situation, in der ich 5000 Proben von einem Gerät alle 0,5 Sekunden verarbeiten muss.Gibt es eine Funktion in Java, um den gleitenden Durchschnitt zu erhalten

Nehmen wir an, die Fenstergröße ist 100, dann würden 50 Punkte aus dem gleitenden Durchschnitt resultieren. Ich versuche mit herkömmlichen Verfahren, d.h. mit Schleifen. Aber das ist ein sehr ineffizienter Weg, dies zu tun. Irgendwelche Vorschläge ?

+4

Haben Sie bereits http://en.wikipedia.org/wiki/Moving_average gelesen, speziell "Weighted Moving Average" –

+0

Ein gewichteter gleitender Durchschnitt ist viel einfacher zu berechnen, viel schneller und in vielen Fällen ein nützlicherer Wert für die Berechnung . d.h. es wird beim Modellieren vieler Systeme verwendet. –

Antwort

8

Sie können dies in O (1) tun: eine Warteschlange der letzten 50 Einträge beibehalten. Wenn Sie einen Eintrag hinzufügen und die Warteschlange um 50 Elemente kürzer ist, aktualisieren Sie einfach die Summe und die Anzahl. Wenn es länger als 50 Elemente ist, aktualisieren Sie auch die Gesamtzahl und die Anzahl. Pseudocode:

add(double x) { 
    total += x; 
    addToQueue(x); 
    if (queueSize > 50) { 
     total -= removeLastFromQueue(); 
    } else { 
     count++; 
    } 
} 
double getAverage() { 
    return total/count; 
} 
23

Überprüfen Sie die Apache Maths Bibliothek. Dies hat Methoden, genau das zu tun, was Sie wollen. Weitere Informationen finden Sie unter DescriptiveStatistics und Mean.

+2

Hallo, ich habe die Bibliothek überprüft. Welche Methode macht den gleitenden Durchschnitt? Ich kann nicht scheinen, es zu finden – Snake

+1

DescriptiveStatistics optional gleitender Durchschnitt – gerardw

18

Hier ist eine Möglichkeit.

public class Rolling { 

    private int size; 
    private double total = 0d; 
    private int index = 0; 
    private double samples[]; 

    public Rolling(int size) { 
     this.size = size; 
     samples = new double[size]; 
     for (int i = 0; i < size; i++) samples[i] = 0d; 
    } 

    public void add(double x) { 
     total -= samples[index]; 
     samples[index] = x; 
     total += x; 
     if (++index == size) index = 0; // cheaper than modulus 
    } 

    public double getAverage() { 
     return total/size; 
    } 
} 

public class RollingTest extends TestCase { 

    private final static int SIZE = 5; 
    private static final double FULL_SUM = 12.5d; 

    private Rolling r; 

    public void setUp() { 
     r = new Rolling(SIZE); 
    } 

    public void testInitial() { 
     assertEquals(0d, r.getAverage()); 
    } 

    public void testOne() { 
     r.add(3.5d); 
     assertEquals(3.5d/SIZE, r.getAverage()); 
    } 

    public void testFillBuffer() { 
     fillBufferAndTest(); 
    } 

    public void testForceOverWrite() { 
     fillBufferAndTest(); 

     double newVal = SIZE + .5d; 
     r.add(newVal); 
     // get the 'full sum' from fillBufferAndTest(), add the value we just added, 
     // and subtract off the value we anticipate overwriting. 
     assertEquals((FULL_SUM + newVal - .5d)/SIZE, r.getAverage()); 
    } 

    public void testManyValues() { 
     for (int i = 0; i < 1003; i++) r.add((double) i); 
     fillBufferAndTest(); 
    } 


    private void fillBufferAndTest() { 
     // Don't write a zero value so we don't confuse an initialized 
     // buffer element with a data element. 
     for (int i = 0; i < SIZE; i++) r.add(i + .5d); 
     assertEquals(FULL_SUM/SIZE, r.getAverage()); 
    } 
} 
+3

Bug: Wenn Sie add Methode weniger als SIZE aufrufen (im Konstruktor angegeben), erhalten Sie falschen Durchschnittswert. Das liegt daran, dass in getAverage() eine Division nach SIZE besteht. Wir brauchen wahrscheinlich einen weiteren Zähler, der sich um diesen Fall kümmert. – sscarduzio

+0

@sscarduzio guten Fang. Ich habe es nicht ausprobiert. Aber der TestOne() Testfall sieht auch fischig aus. Ich nehme an, es könnte genau sein, wenn angenommen wird, dass die Werte in der Liste auf Null initialisiert werden. Das ist jedoch eine ziemlich gequälte Forderung. ;-) –

+2

Es ist nicht nur ungenau für Werte, die kleiner als SIZE sind, dieser Code ist für die letzten SIZE-Werte von * jeder * Menge von Werten ungenau. Verwende nicht. –

4

Hier ist eine gute Umsetzung, BigDecimal mit:

import java.math.BigDecimal; 
import java.math.RoundingMode; 
import java.util.LinkedList; 
import java.util.Queue; 

public class MovingAverage { 

    private final Queue<BigDecimal> window = new LinkedList<BigDecimal>(); 
    private final int period; 
    private BigDecimal sum = BigDecimal.ZERO; 

    public MovingAverage(int period) { 
     assert period > 0 : "Period must be a positive integer"; 
     this.period = period; 
    } 

    public void add(BigDecimal num) { 
     sum = sum.add(num); 
     window.add(num); 
     if (window.size() > period) { 
      sum = sum.subtract(window.remove()); 
     } 
    } 

    public BigDecimal getAverage() { 
     if (window.isEmpty()) return BigDecimal.ZERO; // technically the average is undefined 
     BigDecimal divisor = BigDecimal.valueOf(window.size()); 
     return sum.divide(divisor, 2, RoundingMode.HALF_UP); 
    } 
} 
4

Soweit ich weiß, gibt es keine solche Funktion (Klasse) in Java. Aber Sie können eins selbst machen. Hier ist ein einfaches Beispiel (SMA-Simple Moving Average):

public class MovingAverage { 
    private int [] window; 
    private int n, insert; 
    private long sum; 

    public MovingAverage(int size) { 
     window = new int[size]; 
     insert = 0; 
     sum = 0; 
    } 

    public double next(int val) { 
     if (n < window.length) n++; 
     sum -= window[insert]; 
     sum += val; 
     window[insert] = val; 
     insert = (insert + 1) % window.length; 
     return (double)sum/n; 
    } 
} 
0
static int[] myIntArray = new int[16]; 
public static double maf(double number) 
{ 
    double avgnumber=0; 
    for(int i=0; i<15; i++) 
    { 
     myIntArray[i] = myIntArray[i+1]; 
    } 
    myIntArray[15]= (int) number; 
    /* Doing Average */ 
    for(int i=0; i<16; i++) 
    { 
     avgnumber=avgnumber+ myIntArray[i]; 
    } 
    return avgnumber/16; 

} 

diesen Algorithmus kann auch als Moving Average Filter genannt werden, die gut für mich arbeiten ... implementiert ich dieses algo in meinem Diagramm Projekt !

Verwandte Themen