The simple way to average (or more accurately known as 'find the
arithmetic mean of') a list of numbers is to add them all up and divide by the number of items.
E.g.
The average of: 6, 2, 4, 5, 6, 3, 2 is 4:
6+2+4+5+6+3+2 = 28
28/7=4
Yesterday, we had the need to keep recalculating the average on the fly as more data is received so the user could be constantly informed of the current average. Using the classic algorithm would result in a degradation of performance over time so we chose an alternative.
The basic idea behind the continuous average is to keep track of the total number of items and current average as you go. Then, when a new number comes along work out how far above or below the current average it is and divide it by the number of items so far. This results in an 'adjustment' value that is used to 'correct' the current average to take account of the new value.
Here's a simple C# class that implements the algorithm
public class ContinuousAverage
{
private int count = 0;
private decimal? currentAverage = null ;
public decimal Average
{
get
{
if (currentAverage == null)
throw new InvalidOperationException ("No numbers have been averaged");
return currentAverage.Value;
}
}
public int Count
{
get { return count; }
}
public void Add(decimal number)
{
count++;
if (currentAverage == null)
{
currentAverage = number;
}
else
{
decimal differenceFromAverage = number - currentAverage.Value;
currentAverage += differenceFromAverage / count;
}
}
}
[TestFixture]
public class ContinuousAverageTests
{
[Test]
[ExpectedException(typeof(InvalidOperationException ), "No numbers have been averaged")]
public void TestAverageOfNoItemsThrowsException( )
{
MovingAverage movingAverage = new MovingAverage ();
decimal value = movingAverage.Average;
}
[Test]
public void TestAverageOfOneItemIsItemValue( )
{
MovingAverage movingAverage = new MovingAverage ();
movingAverage.Add( 1.234M );
Assert.AreEqual( 1.234M, movingAverage.Average );
}
[Test]
public void TestAverageOfSeveralItemsIsStandardArithmeticMean()
{
MovingAverage movingAverage = new MovingAverage ();
movingAverage.Add(6);
movingAverage.Add(2);
movingAverage.Add(4);
movingAverage.Add(5);
movingAverage.Add(6);
movingAverage.Add(3);
movingAverage.Add(2);
Assert.AreEqual(7, movingAverage.Count);
Assert.AreEqual(4, movingAverage.Average, "(6+2+4+5+6+3+2)/7 = (28/7) = 4");
}
}