web-dev-qa-db-ja.com

ANSI Cコードの1d線形畳み込み?

車輪の再発明ではなく、ANSI Cの1D線形 畳み込み コードスニペットを誰かに紹介してもらえないでしょうか。グーグルとスタックオーバーフローで検索しましたが、Cで使用できるものが見つかりませんでした。

たとえば、配列A、B、およびCの場合、すべて倍精度です。ここで、AとBは入力で、Cは出力であり、長さはそれぞれlen_Alen_B、およびlen_C = len_A + len_B - 1です。 。

私の配列サイズは小さいので、FFTによる高速畳み込みの実装で速度を上げる必要はありません。簡単な計算を探しています。

12
ggkmath

方法は次のとおりです。

#include <stddef.h>
#include <stdio.h>

void convolve(const double Signal[/* SignalLen */], size_t SignalLen,
              const double Kernel[/* KernelLen */], size_t KernelLen,
              double Result[/* SignalLen + KernelLen - 1 */])
{
  size_t n;

  for (n = 0; n < SignalLen + KernelLen - 1; n++)
  {
    size_t kmin, kmax, k;

    Result[n] = 0;

    kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
    kmax = (n < SignalLen - 1) ? n : SignalLen - 1;

    for (k = kmin; k <= kmax; k++)
    {
      Result[n] += Signal[k] * Kernel[n - k];
    }
  }
}

void printSignal(const char* Name,
                 double Signal[/* SignalLen */], size_t SignalLen)
{
  size_t i;

  for (i = 0; i < SignalLen; i++)
  {
    printf("%s[%zu] = %f\n", Name, i, Signal[i]);
  }
  printf("\n");
}

#define ELEMENT_COUNT(X) (sizeof(X) / sizeof((X)[0]))

int main(void)
{
  double signal[] = { 1, 1, 1, 1, 1 };
  double kernel[] = { 1, 1, 1, 1, 1 };
  double result[ELEMENT_COUNT(signal) + ELEMENT_COUNT(kernel) - 1];

  convolve(signal, ELEMENT_COUNT(signal),
           kernel, ELEMENT_COUNT(kernel),
           result);

  printSignal("signal", signal, ELEMENT_COUNT(signal));
  printSignal("kernel", kernel, ELEMENT_COUNT(kernel));
  printSignal("result", result, ELEMENT_COUNT(result));

  return 0;
}

出力:

signal[0] = 1.000000
signal[1] = 1.000000
signal[2] = 1.000000
signal[3] = 1.000000
signal[4] = 1.000000

kernel[0] = 1.000000
kernel[1] = 1.000000
kernel[2] = 1.000000
kernel[3] = 1.000000
kernel[4] = 1.000000

result[0] = 1.000000
result[1] = 2.000000
result[2] = 3.000000
result[3] = 4.000000
result[4] = 5.000000
result[5] = 4.000000
result[6] = 3.000000
result[7] = 2.000000
result[8] = 1.000000
22
Alexey Frunze

テストされていませんが、うまくいくようです...

void conv(const double v1[], size_t n1, const double v2[], size_t n2, double r[])
{
    for (size_t n = 0; n < n1 + n2 - 1; n++)
        for (size_t k = 0; k < max(n1, n2); k++)
            r[n] += (k < n1 ? v1[k] : 0) * (n - k < n2 ? v2[n - k] : 0);
}

ヒント:ホイールを見つけるよりも車輪の再発明にかかる時間が短い場合は、前者を検討してください。

3
Mehrdad

@Mehrdadのアプローチを使用して、次の回答を作成しました。

void conv(const double v1[], size_t n1, const double v2[], size_t n2, double r[])
{
    for (size_t n = 0; n < n1 + n2 - 1; n++)
        for (size_t k = 0; k < max(n1, n2) && n >= k; k++)
            r[n] += (k < n1 ? v1[k] : 0) * (n - k < n2 ? v2[n - k] : 0);
}

2番目のループでknより大きくなると、インデックスが下限を超えるという問題があるため、それを防ぐための追加の条件があるはずです。

0
Imro Stocky

2つの有限長シーケンスの畳み込みを行っているため、線形畳み込みではなく巡回畳み込みを実行すると、目的の周波数応答が得られます。巡回畳み込みの非常に単純な実装は、Alexによって与えられたアルゴリズムと同じ結果を達成します。

#define MOD(n, N) ((n<0)? N+n : n)
......
......

for(n=0; n < signal_Length + Kernel_Length - 1; n++)
{
    out[n] = 0;
    for(m=0; m < Kernel_Length; m++)
    {
        out[n] = h[m] * x[MOD(n-m, N)];
    }
}
0
Siddhant Raman