Graeme_Grant
Думал, что я брошу свою шляпу на ринг в своем обычном (многоязычном) стиле, но только одно решение... ;)
Одним из преимуществ этого решения, помимо очень небольшого объема памяти, является то, что вы можете использовать один или несколько бесконечных потоков данных, а не только фиксированный массив с фиксированным окном.
using System;
using System.Linq;
namespace RollingSD
{
class Program
{
static void Main(string[] args)
{
double[] data = { 1E30f, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
Console.WriteLine("Running");
Console.WriteLine("-------\r\n");
int count = 0;
var avg = 0.0;
var t = new Tuple<double, double, int>(0.0, 0.0, 0);
for (int i = 0; i < data.Length; i++)
{
avg = avg.Average(data[i], ref count);
Console.WriteLine($"Dataset: {{{string.Join(", ", data.Take(i + 1))}}}");
Console.WriteLine($"Results: Avg = {avg,-20} Std Dev = {data[i].StandardDeviation(ref t),-20}\r\n");
}
Console.WriteLine("\r\n\r\nWindowed");
Console.WriteLine("--------\r\n");
var window = new CircularBuffer<double>(5);
foreach (var item in data)
{
window.Add(item);
Console.WriteLine($"Dataset: {window}");
Console.WriteLine($"Results: Avg = {window.Average(),-20} Std Dev = {window.StandardDeviation(),-20}\r\n");
}
Console.WriteLine("-- Press any ket to exit --");
Console.ReadKey();
}
}
public static class HelperExtensions
{
public static double StandardDeviation(this CircularBuffer<double> data)
{
var s1 = 0.0;
var t = new Tuple<double, double, int>(0.0, 0.0, 0);
for (int i = 0; i < data.Length; i++)
s1 = data[i].StandardDeviation(ref t);
return s1;
}
public static double StandardDeviation(this double value, ref Tuple<double, double, int> t)
{
var c = t.Item3 + 1;
double m, s = m = 0.0;
if (c == 1)
m = value;
else
{
m = t.Item1 + (value - t.Item1) / c;
s = t.Item2 + (value - t.Item1) * (value - m);
}
t = new Tuple<double, double, int>(m, s, c);
return Math.Sqrt(c > 1 ? s / (c - 1) : 0);
}
public static double Average(this CircularBuffer<double> data)
{
var a1 = 0.0;
int count = 0;
for (int i = 0; i < data.Length; i++)
a1 = a1.Average(data[i], ref count);
return a1;
}
public static double Average(this double average, double value, ref int count)
=> (average * count + value) / ++count;
}
public class CircularBuffer<T>
{
T[] buffer;
int nextFree, len;
public CircularBuffer(int length)
{
buffer = new T[length];
nextFree = 0;
}
public void Add(T o)
{
buffer[nextFree] = o;
nextFree = (nextFree + 1) % buffer.Length;
if (len < buffer.Length) len++;
}
public T this[int index]
=> index >= 0 && index <= buffer.Length ? buffer[index] : default(T);
public int Length => len;
public override string ToString()
=> $"{{{string.Join(", ", ToList())}}}";
public IEnumerable<T> ToList()
{
foreach (var item in nextFree < len
? buffer.Skip(nextFree)
.Take(len - nextFree)
.Concat(buffer.Take(nextFree))
: buffer.Take(len))
yield return item;
}
}
}
Imports System.Runtime.CompilerServices
Module Module1
Sub Main()
Dim data As Double() = {1.0E+30F, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
Console.WriteLine("Running")
Console.WriteLine("-------{0}", vbCrLf)
Dim count As Integer = 0
Dim avg As Double = 0F
Dim t = New Tuple(Of Double, Double, Integer)(0.0, 0.0, 0)
For i As Integer = 0 To data.Length - 1
avg = avg.Average(data(i), count)
Console.WriteLine("Dataset: {{{0}}}", String.Join(", ", data.Take(i + 1)))
Console.WriteLine("Results: Avg = {0,-20} Std Dev = {1,-20}{2}",
avg, data(i).StandardDeviation(t), vbCrLf)
Next
Console.WriteLine("{0}{0}Windowed", vbCrLf)
Console.WriteLine("--------{0}", vbCrLf)
Dim window = New CircularBuffer(Of Double)(5)
For Each item In data
window.Add(item)
Console.WriteLine("Dataset: {0}", window)
Console.WriteLine("Results: Avg = {0,-20} Std Dev = {1,-20}{2}",
window.Average(), window.StandardDeviation(), vbCrLf)
Next
Console.WriteLine("-- Press any ket to exit --")
Console.ReadKey()
End Sub
End Module
Module HelperExtensions
<Extension>
Public Function StandardDeviation(data As CircularBuffer(Of Double)) As Double
Dim s1 = 0.0
Dim t = New Tuple(Of Double, Double, Integer)(0.0, 0.0, 0)
For i As Integer = 0 To data.Length - 1
s1 = data(i).StandardDeviation(t)
Next
Return s1
End Function
<Extension>
Public Function StandardDeviation(value As Double, ByRef t As Tuple(Of Double, Double, Integer)) As Double
Dim c = t.Item3 + 1
Dim m = 0.0
Dim s = 0.0
If c = 1 Then
m = value
Else
m = t.Item1 + (value - t.Item1) / c
s = t.Item2 + (value - t.Item1) * (value - m)
End If
t = New Tuple(Of Double, Double, Integer)(m, s, c)
Return Math.Sqrt(IIf(c > 1, s / (c - 1), 0))
End Function
<Extension>
Public Function Average(data As CircularBuffer(Of Double)) As Double
Dim a1 = 0.0
Dim count As Integer = 0
For i As Integer = 0 To data.Length - 1
a1 = a1.Average(data(i), count)
Next
Return a1
End Function
<Extension>
Public Function Average(avg As Double, value As Double, ByRef count As Integer) As Double
count += 1
Dim ret As Double = (avg * (count - 1) + value) / count
Return ret
End Function
End Module
Public Class CircularBuffer(Of T)
Private buffer As T()
Private nextFree As Integer, len As Integer
Public Sub New(length As Integer)
buffer = New T(length - 1) {}
nextFree = 0
End Sub
Public Sub Add(o As T)
buffer(nextFree) = o
nextFree = (nextFree + 1) Mod buffer.Length
If len < buffer.Length Then
len += 1
End If
End Sub
Default Public ReadOnly Property Item(index As Integer) As T
Get
Return If(index >= 0 AndAlso index <= buffer.Length, buffer(index), Nothing)
End Get
End Property
Public ReadOnly Property Length() As Integer
Get
Return len
End Get
End Property
Public Overrides Function ToString() As String
Return String.Format("{{{0}}}", String.Join(", ", ToList()))
End Function
Public Iterator Function ToList() As IEnumerable(Of T)
For Each Item As T In If(nextFree < len,
buffer.Skip(nextFree) _
.Take(len - nextFree) _
.Concat(buffer.Take(nextFree)), buffer.Take(len))
Yield Item
Next
End Function
End Class
Выход:
Running
-------
Dataset: {1.00000001504747E+30}
Results: Avg = 1.00000001504747E+30 Std Dev = 0
Dataset: {1.00000001504747E+30, 1}
Results: Avg = 5.00000007523733E+29 Std Dev = 7.07106791826713E+29
Dataset: {1.00000001504747E+30, 1, 1}
Results: Avg = 3.33333338349155E+29 Std Dev = 5.77350277877284E+29
Dataset: {1.00000001504747E+30, 1, 1, 1}
Results: Avg = 2.50000003761867E+29 Std Dev = 5.00000007523733E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1}
Results: Avg = 2.00000003009493E+29 Std Dev = 4.47213602229389E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1}
Results: Avg = 1.66666669174578E+29 Std Dev = 4.08248296606965E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1, 1}
Results: Avg = 1.42857145006781E+29 Std Dev = 3.77964478696635E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1, 1, 1}
Results: Avg = 1.25000001880933E+29 Std Dev = 3.53553395913357E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1, 1, 1, 1}
Results: Avg = 1.11111112783052E+29 Std Dev = 3.33333338349155E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1, 1, 1, 1, 1}
Results: Avg = 1.00000001504747E+29 Std Dev = 3.16227770775265E+29
Dataset: {1.00000001504747E+30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
Results: Avg = 9.09090922770424E+28 Std Dev = 3.01511349114745E+29
Windowed
--------
Dataset: {1.00000001504747E+30}
Results: Avg = 1.00000001504747E+30 Std Dev = 0
Dataset: {1.00000001504747E+30, 1}
Results: Avg = 5.00000007523733E+29 Std Dev = 7.07106791826713E+29
Dataset: {1.00000001504747E+30, 1, 1}
Results: Avg = 3.33333338349155E+29 Std Dev = 5.77350277877284E+29
Dataset: {1.00000001504747E+30, 1, 1, 1}
Results: Avg = 2.50000003761867E+29 Std Dev = 5.00000007523733E+29
Dataset: {1.00000001504747E+30,1,1,1,1}
Results: Avg = 2.00000003009493E+29 Std Dev = 4.47213602229389E+29
Dataset: {1,1,1,1,1}
Results: Avg = 1 Std Dev = 0
Dataset: {1,1,1,1,1}
Results: Avg = 1 Std Dev = 0
Dataset: {1,1,1,1,1}
Results: Avg = 1 Std Dev = 0
Dataset: {1,1,1,1,1}
Results: Avg = 1