Скользящее среднее в Objective-C

Я пытаюсь понять, как получить скользящее среднее из определенного значения, которое я получаю от своего микрофона. У меня есть функция frequencyChangedWithValue, вызывающая мой метод измерения. Это означает, что я получаю изменение значения частоты до 10 раз в секунду. Теперь мне интересно, как сделать среднее число из всех этих меняющихся значений. Как это сделать?

Код

- (void)frequencyChangedWithValue:(float)newFrequency{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    self.currentFrequency = newFrequency;

    [self performSelectorInBackground:@selector(filterFrequencyToGetBeats) withObject:nil];
    [pool drain];
    pool = nil;
}


- (void) filterFrequencyToGetBeats {

    if (self.currentFrequency > 1000 && pointInTime % 2 == 0)
    {
        tm_start = mach_absolute_time();
        pointInTime = pointInTime + 1;
    }
    else
        if (self.currentFrequency > 1000 && pointInTime % 2 == 1)
        {
            pointInTime = pointInTime + 1;

            tm_end = mach_absolute_time();
            tm_elapsed = tm_end - tm_start;
            mach_timebase_info(&info);
            tm_nanoSeconds = tm_elapsed * info.numer / info.denom;
            tm_milliSeconds = tm_nanoSeconds / (1000000);
            tm_seconds = (tm_milliSeconds/1000);

            fflush(stdout);
            printf ( "| allocateAudio: %5lld milliseconds, (%12lld nano seconds)\n",     tm_milliSeconds, tm_nanoSeconds );
        }
    }

person ivanmarli    schedule 06.02.2013    source источник
comment
См. аналогичный вопрос здесь: stackoverflow.com/q/8447868/456851   -  person sudo rm -rf    schedule 07.02.2013
comment
Поскольку вы не используете ARC, в этом случае pool=nil бесполезен.   -  person Ramy Al Zuhouri    schedule 07.02.2013
comment
На эту тему есть очень подробная статья в Википедии. Как правило, если нет причин для обратного, вы должны использовать экспоненциальную скользящую среднюю. Код настолько прост, что нет необходимости его откуда-то брать.   -  person Hot Licks    schedule 07.02.2013
comment
@RamyAlZuhouri - Но, тем не менее, часто это хорошая практика.   -  person Hot Licks    schedule 07.02.2013
comment
Насколько я понимаю, это скорее избыточность, чем хорошая практика.   -  person Ramy Al Zuhouri    schedule 07.02.2013
comment
@RamyAlZuhouri - вы не должны поддерживать старый код.   -  person Hot Licks    schedule 07.02.2013


Ответы (2)


У меня есть один из них:

// MovingAverage.h

@interface MovingAverage : NSObject

@property (readonly, nonatomic) float movingAverage;
@property (readonly, nonatomic) float cumulativeAverage;

- (id)initWithPeriod:(NSUInteger)period;
- (void)addDatum:(NSNumber *)datum;

@end


// MovingAverage.m

#import "MovingAverage.h"

@interface MovingAverage ()
@property (strong, nonatomic) NSMutableArray *queue;
@property (assign, nonatomic) NSUInteger period;
@property (assign, nonatomic) NSUInteger count;
@property (assign, nonatomic) float movingAverage;
@property (assign, nonatomic) float cumulativeAverage;
@end

@implementation MovingAverage

- (id)initWithPeriod:(NSUInteger)period {

    self = [self init];
    if (self) {
        _period = period;
        // with arc
        _queue = [NSMutableArray array];
        // without arc
        _queue = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)addDatum:(NSNumber *)datum {

    [self.queue insertObject:datum atIndex:0];

    float removed = 0;
    float datumf = [datum floatValue];

    if (self.queue.count > self.period) {
        removed = [[self.queue lastObject] floatValue];
        [self.queue removeLastObject];
    }

    self.movingAverage = self.movingAverage - (removed / self.period) + (datumf / self.period);

    // compute the cumulative average
    self.cumulativeAverage = self.cumulativeAverage + (datumf - self.cumulativeAverage) / ++self.count;
}

// if non-ARC
- (void)dealloc {
    [_queue release];
    [super dealloc];
}

@end
person danh    schedule 06.02.2013
comment
Я думаю, вам может понадобиться инициализировать вашу очередь (NSMutableArray) где-то в вашем конструкторе. - person Erwan; 30.09.2013
comment
Хорошая точка зрения. У меня есть ленивая инициализация в геттере, которую я не вставил сюда. Добавлю в инит. - person danh; 30.09.2013
comment
Хорошо, но для не-Arc вы освобождаете очередь в Dealloc (что хорошо для меня), но на самом деле она не сохраняется при создании. Вы можете рассмотреть: _queue = [[NSMutableArray alloc] initWithCapacity:_period]; вместо _queue = [массив NSMutableArray] - person Erwan; 02.10.2013
comment
правильно снова. я забыл, что у меня был вариант без дуги внизу. - person danh; 02.10.2013

Вы можете просто сделать это, удерживая массив и общее количество элементов. Поскольку это скользящее среднее, количество элементов может быть больше, чем общее количество элементов массива, и вам необходимо вычислить остаток от этого количества.

Например:

@property (nonatomic,retain) NSMutableArray* elements;   
@property (nonatomic,assign) NSUInteger lastIndex;
@property (nonatomic,assign) double average;

Инициализируйте элементы и заставьте их содержать N значений (NSNumber длинных значений). Вы должны иметь возможность изменить среднее значение, просто вызвав простой метод, подобный этому:

- (void) changeAverage: (long) newValue
{
    NSUInteger index= (++lastIndex) % elements.count;
    NSNumber* oldValue= elements[index]; // same as [elements objectAtIndex: index];
    average+= (double)newValue/elements.count -  (double)[oldValue longValue]/elements.count;   
    // The cast is to don't lose the decimal precision while dividing these numbers.
    [elements replaceObjectAtIndex: index withObject: @(newValue)];  
    // @(newValue) is the same as [NSNumber numberWithLong: newValue];
}
person Ramy Al Zuhouri    schedule 06.02.2013