ANSI C 代码中的一维线性卷积?

发布于 2024-12-20 06:13:16 字数 335 浏览 1 评论 0原文

我不知道是否有人可以向我推荐 ANSI C 中的一维线性 卷积 代码片段,而不是重新发明轮子?我在谷歌和堆栈溢出中进行了搜索,但找不到 CI 中可以使用的任何内容。

例如,对于数组 A、B 和 C,均为双精度,其中 A 和 B 是输入,C 是输出,长度为 len_Alen_B 和 <分别为:code>len_C = len_A + len_B - 1。

我的数组尺寸很小,因此不需要通过 FFT 实现快速卷积来提高速度。寻找简单的计算。

Rather than reinvent the wheel, I wonder if anyone could refer me to a 1D linear convolution code snippet in ANSI C? I did a search on google and in stack overflow, but couldn't find anything in C I could use.

For example, for Arrays A, B, and C, all double-precision, where A and B are inputs and C is output, having lengths len_A, len_B, and len_C = len_A + len_B - 1, respectively.

My array sizes are small and so any speed increase in implementing fast convolution by FFT is not needed. Looking for straightforward computation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何处潇湘 2024-12-27 06:13:16

方法如下:

#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

Here's how:

#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;
}

Output:

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
云裳 2024-12-27 06:13:16

未经测试,但看起来它会起作用...

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);
}

提示:如果重新发明轮子比找到一个轮子花费的时间更少,请考虑前者。

Not tested, but it seems like it would work...

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);
}

Tip: If it takes less time to reinvent a wheel than to find one, do consider the former.

耶耶耶 2024-12-27 06:13:16

由于我们正在对 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)];
    }
}

Since, we are taking convolution of 2 finite length sequences, hence the desired frequency response is achieved if circular convolution is performed rather than linear convolution. A very simple implementation of circular convolution will achieve the same result as the algorithm given by 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)];
    }
}
兮子 2024-12-27 06:13:16

我使用了@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);
}

There's Problem with index Beyond lowerbound when in secondloops k gets big than n, so,猜测应该有额外的条件来防止这种情况发生。

I used @Mehrdad's approach, and created the following anwer:

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);
}

There's problem with index exceeding lower bound when in second loops k gets bigger than n, so, guess there should be extra condition to prevent that.

我不会写诗 2024-12-27 06:13:16

这是我专注于可读性的简单解决方案

int lenh = 4;
double *h = new double[lenh] {2, 4, -1, 1 };
int lenx = 7;
double *x = new double[lenx] {-1, 2, 3, -2, 0, 1, 2 };
int leny = lenx + lenh - 1;
double *y = new double[leny];

for (int i = 0; i < leny; i++)
{
    y[i] = 0;                       // set to zero before sum
    for (int j = 0; j < lenh; j++)
    {
        if (i - j >= 0 && i - j < lenx)
        {
            y[i] += x[i - j] * h[j];    // convolve: multiply and accumulate
        }
        
    }
    std::cout << y[i] << std::endl;
}

This is my simple solution focused on readability

int lenh = 4;
double *h = new double[lenh] {2, 4, -1, 1 };
int lenx = 7;
double *x = new double[lenx] {-1, 2, 3, -2, 0, 1, 2 };
int leny = lenx + lenh - 1;
double *y = new double[leny];

for (int i = 0; i < leny; i++)
{
    y[i] = 0;                       // set to zero before sum
    for (int j = 0; j < lenh; j++)
    {
        if (i - j >= 0 && i - j < lenx)
        {
            y[i] += x[i - j] * h[j];    // convolve: multiply and accumulate
        }
        
    }
    std::cout << y[i] << std::endl;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文