我有一种情况需要每隔0.5秒从设备处理5000个样本。
假设窗口大小为100,那么移动平均线将产生50个点。我正在尝试使用传统方法,即使用循环。但这是一种非常低效的方法。有什么建议吗?
答案 0 :(得分:24)
查看Apache Maths库。这有方法可以准确地做你想要的。有关详细信息,请参阅DescriptiveStatistics和Mean。
答案 1 :(得分:20)
这是一种方式。
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());
}
}
答案 2 :(得分:8)
您可以在O(1)中执行此操作:保留最近50个条目的队列。添加条目并且队列短50个元素时,只需更新总计和计数。如果它超过50个元素,也要更新总数和计数。伪代码:
add(double x) {
total += x;
addToQueue(x);
if (queueSize > 50) {
total -= removeLastFromQueue();
} else {
count++;
}
}
double getAverage() {
return total / count;
}
答案 3 :(得分:5)
据我所知,Java中没有这样的函数(类)。但你可以自己做一个。这是一个简单的例子(SMA-简单移动平均线):
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;
}
}
答案 4 :(得分:4)
这是一个很好的实现,使用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);
}
}
答案 5 :(得分:2)
Java 8已添加java.util.IntSummaryStatistics
。 Double
和Long
也有类似的类。相当简单易用:
IntSummaryStatistics stats = new IntSummaryStatistics();
stats.accept(1);
stats.accept(3);
stats.getAverage(); // Returns 2.0
答案 6 :(得分: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;
}
这个算法也可以称为移动平均滤波器,对我来说效果很好......我在我的图形项目中实现了这个算法!