计算可变参数的移动平均值

发布于 2024-12-20 05:27:31 字数 204 浏览 2 评论 0原文

我有一个整数属性,每秒更新一次,信号强度值范围为 0 - 100。

我希望能够持续测量最近 10、25、50 次测量的移动平均值。

做到这一点最有效的方法是什么?

我目前正在考虑使用 NSMutableArray 实现一组 FIFO 队列,并在数组具有所需数量的条目后每次在末尾添加新队列时弹出主值。但是,我不确定是否有更有效的方法来做到这一点。

I have an integer property which is updated every second with a signal-strength value ranging from 0 - 100.

I'd like to be able to keep an ongoing measure of the moving average over the last 10, 25, 50 measurements.

What's the most efficient way of doing this?

I'm currently thinking of implementing a set of FIFO queues using NSMutableArray and popping the leading value every time I add a new one at the end once the array has the requisite number of entries. However, I'm not sure if there's a more efficient way of doing this or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

π浅易 2024-12-27 05:27:31

我编写了一个名为 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

I wrote a simple class called MovingAverage to handle this. You init the method with the number of periods to maintain and it keeps track of the rest using the modulus of the sample count to know which of the static slots to put it in.

Initialize with

MovingAverage *avg5periods = [[MovingAverage alloc] initWithSize:5];

Add items:

[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

Header file:

#import <Foundation/Foundation.h>

@interface MovingAverage : NSObject {
    NSMutableArray *samples;
    int sampleCount;
    int averageSize;
}
-(id)initWithSize:(int)size;
-(void)addSample:(double)sample;
-(double)movingAverage;
@end

and the implementation file:

#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
晚雾 2024-12-27 05:27:31

排队才是正确的方式。真正的效率取决于您如何重新计算平均值。

应该这样做:

avg = avg + newSample/N - [queue dequeue]/N
[queue enqueue:newSample]

即新的运行平均值只是旧平均值减去您删除的最旧值的权重,加上您排队的最新值的权重。

A queue is the right way. The real efficiency comes with how you recalculate the average.

It should be done with:

avg = avg + newSample/N - [queue dequeue]/N
[queue enqueue:newSample]

i.e. the new running average is simply the old average minus the weight of the oldest value you dropped, plus the weight of the newest value you enqueued.

花开柳相依 2024-12-27 05:27:31

我认为你有正确的解决方案。

如果您确实关心性能,那么您可以使用静态大小的数组并跟踪当前索引,而不是将内容移入和移出动态调整大小的数组。

即,如果 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 如何处理内存分配。

I think you've got the right solution.

If you really care about performance, instead of moving things in and out of a dynamically resized array you could use an array of static size and keep track of the current index.

I.e. if N is the size the array and % is the modulo operator (I'm not an objective C programmer):

values[current] = get_current_sample()
previous = (current + N - 1) % N
sum = sum + values[current] - values[previous]
current = (current + 1) % N

The average = sum / N. You have to treat the warm up period separately (before you have N samples).

This could be much faster depending on how NSMutableArray handles memory allocation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文