Java计算简单移动平均时多余首元素、数组越界问题求解
问题根因
两个问题本质都是没有区分环形缓冲区的填充阶段和有效计算阶段:
- 首个无效值6.0:buffer数组初始化时默认填充0,第一次循环仅写入了第一个元素
12/2=6,剩余buffer位还是初始0,此时窗口尚未攒够n=2个有效数据,计算出的结果自然无效。 - 数组下标越界:原逻辑每遍历一个输入数据就往output数组写一个值,但有效结果总长度为
data.length -n +1,当写入位置超过output数组长度时就会触发越界。
修正实现
核心调整逻辑:
- 输出数组直接按有效长度
data.length -n +1初始化,不用先开长数组再剔除无效值 - 前n-1个输入数据仅写入环形缓冲区,不触发结果计算和输出
- 从第n个数据(对应下标i=n-1)开始,每写入一个缓冲区数据就计算一次移动平均值,写入输出数组
- 单独维护输出数组的写入下标,不和输入遍历下标混用
修正后可直接运行的代码:
public class SimpleMovingAverage { public static void main(String[] args) { int windowSize = 2; double[] inputData = {12, 13, 15, 7, 6, 9, 13}; // 基础参数校验 if (windowSize < 1 || windowSize > inputData.length) { throw new IllegalArgumentException("窗口大小必须在1到输入数据长度之间"); } double[] ringBuffer = new double[windowSize]; double[] result = new double[inputData.length - windowSize + 1]; int bufferPos = 0; int resultPos = 0; for (int i = 0; i < inputData.length; i++) { // 最新数据写入环形缓冲区 ringBuffer[bufferPos] = inputData[i] / windowSize; bufferPos = (bufferPos + 1) % windowSize; // 窗口填满后才开始计算有效均值 if (i >= windowSize - 1) { double ma = 0.0; for (double num : ringBuffer) { ma += num; } result[resultPos++] = ma; } } // 验证输出,结果为[12.5, 14.0, 11.0, 6.5, 7.5, 11.0],与预期一致 for (double val : result) { System.out.println(val); } } }
可选优化
如果数据量较大,可以维护一个实时累加的总和变量,不用每次计算MA都遍历整个缓冲区求和,把时间复杂度从O(m*n)降到O(m)(m为输入数据长度):
// 优化后求和逻辑,无需内层循环 double total = 0.0; for (int i = 0; i < inputData.length; i++) { // 先减去缓冲区里即将被覆盖的旧值 total -= ringBuffer[bufferPos]; ringBuffer[bufferPos] = inputData[i] / windowSize; // 加上新写入的值 total += ringBuffer[bufferPos]; bufferPos = (bufferPos + 1) % windowSize; if (i >= windowSize -1) { result[resultPos++] = total; } }
内容的提问来源于stack exchange,提问作者judo_ürgens
相关产品推荐
相关产品推荐

