私はHaskellでFIRフィルタを実装しました。私はそれほどFIRフィルターについて知らず、私のコードは既存のC#実装に大きく基づいています。したがって、私の実装はC#スタイルがあまりにも多く、実際にはハスケルのようなものではないと感じています。私は、コードを実装するためにもっと慣用的な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_)しかかかりません。配列の1つが小さい場合、Daniel Martinのバージョンは問題ありません。 – leftaroundabout
はい、そうですよ!私はFFTの使用を検討することを考えます。 –