Efr*_*ain 4 .net c# linq filtering signal-processing
我正在寻找一种优雅的方法来在c#中实现移动平均滤波器。现在,这将很容易,但是在边界处,平均窗口将环绕开始/结束。这种方法使我的代码难看且不直观,我想知道是否存在使用LINQ来解决此问题的更明智的方法。
所以我目前拥有的是:
// input is a List<double> y, output is List<double> yfiltered
int yLength = y.Count;
for (int i = 0; i < yLength; i++)
{
double sum = 0.0;
for (int k = i - halfWindowWidth; k <= i + halfWindowWidth; k++)
{
if (k < 0)
{
// k is negative, wrap around
sum += y[yLength - 1 + k];
}
else if (k >= yLength)
{
// k exceeds y length, wrap around
sum += y[k - yLength];
}
else
{
// k within y.Count
sum += y[k];
}
}
yfiltered[i] = sum / (2 * halfWindowWidth + 1);
}
Run Code Online (Sandbox Code Playgroud)
这是一个完全不同的建议-
我实际上是试图使其更好,而不是更具可读性。
当前代码的问题是,在不需要时,它会一次又一次地累加许多数字。
在实现代码之后比较两种方法...
我只是第一次加一堆,然后一次又一次地减去尾部再加上头部:
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);
}
Run Code Online (Sandbox Code Playgroud)
这是速度测试,将完全重新计算方法与该方法进行了比较:
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));
}
Run Code Online (Sandbox Code Playgroud)
时间复杂度:
我的方式:O(n + m)
其他方式:O(n * m)
由于Foo1为O(n * m),而Foo2为O(n + m),因此差异之大确实不足为奇。
在这个并不十分疯狂的规模上的结果是:
结果相等:正确
Foo1:5.52秒
Foo2:61.1毫秒
并且在更大的范围内(将迭代次数和计数都替换为1000和10000):
Foo1:10分钟后停止...
Foo2:6.9秒
| 归档时间: |
|
| 查看次数: |
2489 次 |
| 最近记录: |