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が配列のサイズで、%がモジュロ演算子の場合(私はObjective Cプログラマーではありません):
values[current] = get_current_sample()
previous = (current + N - 1) % N
sum = sum + values[current] - values[previous]
current = (current + 1) % N
平均=合計/N。ウォームアップ期間を個別に処理する必要があります(サンプルがN個になる前)。
これは、NSMutableArrayがメモリ割り当てを処理する方法によってははるかに高速になる可能性があります。