我有一个整数属性,它每秒更新一次,信号强度值范围为 0 - 100。
我希望能够持续测量过去 10、25、50 次测量的移动平均值。
这样做最有效的方法是什么?
我目前正在考虑使用 NSMutableArray 实现一组 FIFO 队列,并在数组具有必要数量的条目后,每次在末尾添加一个新队列时弹出前导值。但是,我不确定是否有更有效的方法来做到这一点。
我有一个整数属性,它每秒更新一次,信号强度值范围为 0 - 100。
我希望能够持续测量过去 10、25、50 次测量的移动平均值。
这样做最有效的方法是什么?
我目前正在考虑使用 NSMutableArray 实现一组 FIFO 队列,并在数组具有必要数量的条目后,每次在末尾添加一个新队列时弹出前导值。但是,我不确定是否有更有效的方法来做到这一点。
我编写了一个名为 MovingAverage 的简单类来处理这个问题。您使用要维护的周期数来初始化该方法,并使用样本计数的模数来跟踪其余部分,以了解将其放入哪个静态插槽。
初始化
MovingAverage *avg5periods = [[MovingAverage alloc] initWithSize:5];
添加项目:
[avg5periods addSample:1.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //1.0
[avg5periods addSample:2.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //1.5
[avg5periods addSample:3.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //2.0
[avg5periods addSample:4.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //2.5
[avg5periods addSample:5.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //3.0
[avg5periods addSample:6.0];
NSLog(@"1.2f",[avg5periods movingAverage]); //4.0
头文件:
#import <Foundation/Foundation.h>
@interface MovingAverage : NSObject {
NSMutableArray *samples;
int sampleCount;
int averageSize;
}
-(id)initWithSize:(int)size;
-(void)addSample:(double)sample;
-(double)movingAverage;
@end
和实现文件:
#import "MovingAverage.h"
@implementation MovingAverage
-(id)initWithSize:(int)size {
if (self = [super init]) {
samples = [[NSMutableArray alloc] initWithCapacity:size];
sampleCount = 0;
averageSize = size;
}
return self;
}
-(void)addSample:(double)sample {
int pos = fmodf(sampleCount++, (float)averageSize);
[samples setObject:[NSNumber numberWithDouble:sample] atIndexedSubscript:pos];
}
-(double)movingAverage {
return [[samples valueForKeyPath:@"@sum.doubleValue"] doubleValue]/(sampleCount > averageSize-1?averageSize:sampleCount);
}
@end
排队是正确的方式。真正的效率来自于你如何重新计算平均值。
应该这样做:
avg = avg + newSample/N - [queue dequeue]/N
[queue enqueue:newSample]
即新的运行平均值只是旧平均值减去您丢弃的最旧值的权重,再加上您排队的最新值的权重。
我认为你有正确的解决方案。
如果您真的关心性能,而不是将事物移入和移出动态调整大小的数组,您可以使用静态大小的数组并跟踪当前索引。
即如果 N 是数组的大小并且 % 是模运算符(我不是一个客观的 C 程序员):
values[current] = get_current_sample()
previous = (current + N - 1) % N
sum = sum + values[current] - values[previous]
current = (current + 1) % N
平均值 = sum / N。您必须分别处理预热期(在您有 N 个样本之前)。
这可能会快得多,具体取决于 NSMutableArray 如何处理内存分配。