我在Haskell中實現了FIR濾波器。我不太瞭解FIR濾波器,我的代碼主要基於現有的C#實現。因此,我感覺我的實現有太多的C#風格,並不像Haskell那樣。我想知道是否有更實用的Haskell實現我的代碼的方式。理想情況下,我很幸運可以實現算法的高階函數(地圖,過濾器,摺疊等)的組合。使用矢量實現FIR濾波器
我的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
順便說一句,這種操作稱爲[卷積(https://en.wikipedia.org/wiki/Convolution)。在_O_(_n_²)窗體中,如果兩個輸入數組都很大,則無論語言如何,這個窗體總是很慢。因此,行業標準使用基於[FFT](https://en.wikipedia.org/wiki/Fast_Fourier_transform)的_fast卷積,其僅使用_O_(_n_·log_n_)。如果其中一個陣列很小,Daniel Martin的版本應該沒問題。 – leftaroundabout
是的,你是對的!我會考慮使用FFT進行研究。 –