How to calculate simple moving average faster in C#?

后端 未结 14 2509
囚心锁ツ
囚心锁ツ 2020-12-15 06:42

What is the fastest library/algorithm for calculating simple moving average? I wrote my own, but it takes too long on 330 000 items decimal dataset.

  • period /
相关标签:
14条回答
  • 2020-12-15 07:12
        public class MovingAverage  
        {
            private Queue<Decimal> samples = new Queue<Decimal>();
            private int windowSize = 16;
            private Decimal sampleAccumulator;
            public Decimal Average { get; private set; }
    
            /// <summary>
            /// Computes a new windowed average each time a new sample arrives
            /// </summary>
            /// <param name="newSample"></param>
            public void ComputeAverage(Decimal newSample)
            {
                sampleAccumulator += newSample;
                samples.Enqueue(newSample);
    
                if (samples.Count > windowSize)
                {
                    sampleAccumulator -= samples.Dequeue();
                }
    
                Average = sampleAccumulator / samples.Count;
            }
        }
    
    0 讨论(0)
  • 2020-12-15 07:14

    My MovingAverage class implementation is:

    • Thread safe
    • Lock free
    • limited to windowSize that is power of two

    Here is the class:

    using System;
    using System.Linq;
    using System.Threading;
    
    public class MovingAverage
    {
        private readonly int _mask;
        private readonly double?[] _values;
        private int _nextIndex = -1;
    
        public MovingAverage(int windowSize)
        {
            _mask = windowSize - 1;
            if (windowSize == 0 || (windowSize & _mask) != 0)
            {
                throw new ArgumentException("Must be power of two", nameof(windowSize));
            }
            _values = new double?[windowSize];
        }
    
        public void Add(double newValue)
        {
            var index = Interlocked.Increment(ref _nextIndex) & _mask;
            _values[index] = newValue;
        }
    
        public double ComputeAverage()
        {
            return _values.TakeWhile(x => x.HasValue)
                .Select(x => x ?? 0)
                .DefaultIfEmpty(0)
                .Average();
        }
    }
    

    here is the NUnit test

    using NUnit.Framework;
    
    public class MovingAverageTest
    {
        [Test]
        public void Should_compute_average()
        {
            var sut = new MovingAverage(4);
    
            Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
            sut.Add(2);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
            sut.Add(4);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
            sut.Add(0);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
            sut.Add(6);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
            sut.Add(6);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(4));
            sut.Add(0);
            sut.Add(0);
            sut.Add(0);
            sut.Add(0);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
            sut.Add(10);
            sut.Add(10);
            sut.Add(10);
            sut.Add(10);
            Assert.That(sut.ComputeAverage(), Is.EqualTo(10));
        }
    
        [Test]
        public void Should_check_windowsize_param()
        {
            Assert.That(() => new MovingAverage(3), Throws.ArgumentException);
        }
    }
    
    0 讨论(0)
提交回复
热议问题