Here is a completely different sentence -
I tried to make it better, not more readable.
The problem with your current code is that it sums a lot of numbers over and over again when it really is not needed.
Comparing both approaches after implementation code ...
I just summarize the bunch for the first time, and then subtract the tail and add the head again and again:
double sum = 0; // sum = Enumerable.Range(i - halfWindowWidth, halfWindowWidth * 2 + 1) // .Select(k => y[(k + yLength) % yLength]).Sum(); for (var i = -halfWindowWidth; i <= halfWindowWidth; i++) { sum += y[(i + yLength) % yLength]; } yFiltered[0] = sum / (2 * halfWindowWidth + 1); for (var i = 1; i < yLength; i++) { sum = sum - y[(i - halfWindowWidth - 1 + yLength) % yLength] + y[(i + halfWindowWidth) % yLength]; yFiltered[i] = sum / (2 * halfWindowWidth + 1); }
And here are the speed tests comparing the full recount approach to this:
private static double[] Foo1(IList<double> y, int halfWindowWidth) { var yfiltered = new double[y.Count]; var yLength = y.Count; for (var i = 0; i < yLength; i++) { var sum = 0.0; for (var k = i - halfWindowWidth; k <= i + halfWindowWidth; k++) { sum += y[(k + yLength) % yLength]; } yfiltered[i] = sum / (2 * halfWindowWidth + 1); } return yfiltered; } private static double[] Foo2(IList<double> y, int halfWindowWidth) { var yFiltered = new double[y.Count]; var windowSize = 2 * halfWindowWidth + 1; double sum = 0; for (var i = -halfWindowWidth; i <= halfWindowWidth; i++) { sum += y[(i + y.Count) % y.Count]; } yFiltered[0] = sum / windowSize; for (var i = 1; i < y.Count; i++) { sum = sum - y[(i - halfWindowWidth - 1 + y.Count) % y.Count] + y[(i + halfWindowWidth) % y.Count]; yFiltered[i] = sum / windowSize; } return yFiltered; } private static TimeSpan TestFunc(Func<IList<double>, int, double[]> func, IList<double> y, int halfWindowWidth, int iteration { var sw = Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) { var yFiltered = func(y, halfWindowWidth); } sw.Stop(); return sw.Elapsed; } private static void RunTests() { var y = new List<double>(); var rand = new Random(); for (var i = 0; i < 1000; i++) { y.Add(rand.Next()); } var foo1Res = Foo1(y, 100); var foo2Res = Foo2(y, 100); Debug.WriteLine("Results are equal: " + foo1Res.SequenceEqual(foo2Res)); Debug.WriteLine("Foo1: " + TestFunc(Foo1, y, 100, 1000)); Debug.WriteLine("Foo2: " + TestFunc(Foo2, y, 100, 1000)); }
Temporary difficulties:
MyWay: O (n + m)
OtherWay: O (n * m)
Since Foo1 is O (n * m) and Foo2 is O (n + m), it is not surprising that the difference is huge.
The results of this not very crazy big scale:
Results are: True
Foo1: 5.52 seconds
Foo2: 61.1 milliseconds
And on a larger scale (replacing 1000 by 10000 on iterations and counts):
Foo1: stopped after 10 minutes ...
Foo2: 6.9 seconds