【发布时间】:2017-04-22 10:52:58
【问题描述】:
我在 Haskell 中实现了 FIR 滤波器。我对 FIR 滤波器了解不多,而且我的代码很大程度上基于现有的 C# 实现。因此,我有一种感觉,我的实现有太多的 C# 风格,而不是真正的 Haskell 风格。我想知道是否有更惯用的 Haskell 方式来实现我的代码。理想情况下,我很幸运能够实现算法的一些高阶函数(映射、过滤器、折叠等)的组合。
我的 Haskell 代码如下所示:
applyFIR :: Vector Double -> Vector Double -> Vector Double
applyFIR b x = generate (U.length x) help
where
help i = if i >= (U.length b - 1) then loop i (U.length b - 1) else 0
loop yi bi = if bi < 0 then 0 else b !! bi * x !! (yi-bi) + loop yi (bi-1)
vec !! i = unsafeIndex vec i -- Shorthand for unsafeIndex
此代码基于以下 C# 代码:
public float[] RunFilter(double[] x)
{
int M = coeff.Length;
int n = x.Length;
//y[n]=b0x[n]+b1x[n-1]+....bmx[n-M]
var y = new float[n];
for (int yi = 0; yi < n; yi++)
{
double t = 0.0f;
for (int bi = M - 1; bi >= 0; bi--)
{
if (yi - bi < 0) continue;
t += coeff[bi] * x[yi - bi];
}
y[yi] = (float) t;
}
return y;
}
如您所见,它几乎是一个直接的副本。我怎样才能把我的实现变成一个更像 Haskell 的实现?你有什么想法?我唯一能想到的就是使用Vector.generate。
我知道DSP 库有一个可用的实现。但它使用列表,对于我的用例来说太慢了。这个Vector 实现比DSP 中的实现要快得多。
我也尝试使用Repa 实现算法。它比Vector 实现更快。结果如下:
applyFIR :: V.Vector Float -> Array U DIM1 Float -> Array D DIM1 Float
applyFIR b x = R.traverse x id (\_ (Z :. i) -> if i >= len then loop i (len - 1) else 0)
where
len = V.length b
loop :: Int -> Int -> Float
loop yi bi = if bi < 0 then 0 else (V.unsafeIndex b bi) * x !! (Z :. (yi-bi)) + loop yi (bi-1)
arr !! i = unsafeIndex arr i
【问题讨论】:
-
顺便说一句,这个操作叫做convolution。在您在这里的 O (n ²) 形式中,如果两个输入数组都很大,则无论语言如何,这总是很慢。因此,行业标准是使用基于FFT 的快速卷积,它只需要O (n · log n)。如果其中一个数组很小,但 Daniel Martin 的版本应该没问题。
-
是的,你是对的!我会考虑使用 FFT。
标签: algorithm haskell signal-processing