Ответ 1
Ознакомьтесь с библиотекой Apache Maths. У этого есть методы для того, чтобы делать именно то, что Вы хотите. См. DescriptiveStatistics и Mean для получения дополнительной информации. Информация.
У меня есть ситуация, когда мне нужно обрабатывать 5000 образцов с устройства каждые 0,5 секунды.
Предположим, что размер окна равен 100, тогда будет 50 очков в результате скользящей средней. Я пытаюсь использовать обычный метод, т.е. С циклами. Но это очень неэффективный способ сделать это. Любые предложения?
Ознакомьтесь с библиотекой Apache Maths. У этого есть методы для того, чтобы делать именно то, что Вы хотите. См. DescriptiveStatistics и Mean для получения дополнительной информации. Информация.
Вот один из способов.
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());
}
}
Вы можете сделать это в O (1): сохранить очередь из последних 50 записей. Когда вы добавляете запись, а очередь меньше 50 элементов, просто обновляйте общее количество и количество. Если оно больше 50 элементов, обновите общее количество и счетчик. Псевдокод:
add(double x) {
total += x;
addToQueue(x);
if (queueSize > 50) {
total -= removeLastFromQueue();
} else {
count++;
}
}
double getAverage() {
return total / count;
}
Здесь хорошая реализация, использующая BigDecimal:
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);
}
}
Насколько я знаю, такой функции (класса) в Java нет. Но вы можете сделать это самостоятельно. Вот простой пример (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;
}
}
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;
}
этот алгоритм также можно назвать фильтром Moving Average, который хорошо работает для меня... я реализовал этот алгоритм в моем графическом проекте!