2010-09-25 27 views
16

Tôi có một tình huống mà tôi cần xử lý 5000 mẫu từ thiết bị sau mỗi 0,5 giây.Có chức năng nào trong java để di chuyển trung bình

Cho phép nói kích thước cửa sổ là 100, sau đó sẽ có 50 điểm do trung bình di chuyển. Tôi đang thử với phương pháp thông thường, tức là với các vòng lặp. Nhưng đây là một cách rất không hiệu quả để làm điều đó. Bất kỳ đề xuất ?

+4

Bạn đã đọc http://en.wikipedia.org/wiki/Moving_average, đặc biệt là "Trung bình di chuyển có trọng số" –

+0

Một trung bình di chuyển có trọng số đơn giản hơn nhiều để tính toán, nhanh hơn nhiều và trong nhiều trường hợp giá trị hữu ích hơn để tính . tức là nó được sử dụng trong việc mô hình hóa nhiều hệ thống. –

Trả lời

8

Bạn có thể làm điều đó trong O (1): giữ một hàng đợi trong 50 mục nhập cuối cùng. Khi bạn thêm một mục nhập và hàng đợi ngắn hơn 50 phần tử, chỉ cần cập nhật tổng số và số đếm. Nếu nó dài hơn 50 phần tử, hãy cập nhật tổng số và đếm. Mã Pseudocode:

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

Kiểm tra thư viện Apache Maths. Điều này có phương pháp để làm chính xác những gì bạn muốn. Xem DescriptiveStatisticsMean để biết thêm thông tin.

+2

Xin chào, tôi đã kiểm tra thư viện. Phương pháp nào di chuyển trung bình? I cant dường như tìm thấy nó – Snake

+1

DescriptiveStatistics tùy chọn di chuyển trung bình – gerardw

18

Đây là một cách.

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

Lỗi: nếu bạn gọi phương thức thêm ít hơn SIZE (được chỉ định trong hàm tạo), bạn nhận được giá trị trung bình sai. Đó là bởi vì có một phân chia bởi SIZE trong getAverage(). Có thể chúng tôi cần một bộ đếm khác để xử lý trường hợp này. – sscarduzio

+0

@sscarduzio nắm bắt tốt. Tôi đã không thử nó. Nhưng TestOne() thử nghiệm trường hợp trông fishy, ​​quá. Tôi cho rằng nó có thể chính xác nếu các giá trị trong danh sách được giả định là được khởi tạo bằng không. Đó là một yêu cầu khá tra tấn, tuy nhiên. ;-) –

+2

Nó không chỉ không chính xác đối với các bộ giá trị nhỏ hơn SIZE, mã này không chính xác cho các giá trị SIZE cuối cùng của * mỗi * bộ giá trị. Không được dùng. –

4

Dưới đây là một việc thực hiện tốt, sử dụng 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); 
    } 
} 
4

Theo như tôi biết, không có chức năng như vậy (class) trong Java. Nhưng bạn có thể tự mình làm một cái. Dưới đây là một ví dụ đơn giản (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; 

} 

thuật toán này cũng có thể được gọi là Moving Average Lọc mà là làm việc tốt cho tôi ... tôi thực hiện algo này trong dự án đồ thị của tôi !

Các vấn đề liên quan