畳み込み積分とは?定義や意味、応用について解説

畳み込み積分(convolution integral)は、2つの関数を組み合わせて新しい関数を作る演算です。信号処理、画像処理、確率論、微分方程式など、理工学の極めて幅広い分野で登場する基本的な概念です。

本記事では、畳み込み積分の定義から直感的な意味、フーリエ変換との重要な関係、そしてPythonでの実装まで解説します。

本記事の内容

  • 畳み込み積分の定義と直感的な理解
  • 畳み込みの性質
  • フーリエ変換との関係(畳み込み定理)
  • 信号処理での応用
  • Pythonでの実装と可視化

畳み込み積分の定義

2つの関数 $f(t)$ と $g(t)$ の畳み込み(convolution)は、次のように定義されます。

$$ (f * g)(t) = \int_{-\infty}^{\infty} f(\tau) g(t – \tau) \, d\tau $$

記号 $*$ は畳み込み演算を表します(乗算ではありません)。

直感的な理解

畳み込みの意味を直感的に理解しましょう。

$(f * g)(t)$ の計算過程を分解すると、

  1. $g(\tau)$ を時間反転して $g(-\tau)$ にする
  2. $t$ だけ平行移動して $g(t – \tau)$ にする
  3. $f(\tau)$ と $g(t – \tau)$ を掛け合わせる
  4. 全区間で積分する

つまり、$g$ を「裏返して」$f$ の上をスライドさせながら、重なり具合の面積を計算していくイメージです。

大雑把に言うと、畳み込みは「ある関数(入力信号)を別の関数(フィルタ)で加重平均する操作」です。

畳み込みの性質

可換律

$$ f * g = g * f $$

$\tau’ = t – \tau$ と置換すると示せます。

結合律

$$ (f * g) * h = f * (g * h) $$

分配律

$$ f * (g + h) = f * g + f * h $$

デルタ関数との畳み込み

ディラックのデルタ関数 $\delta(t)$ は畳み込みの単位元です。

$$ f * \delta = f $$

$$ \int_{-\infty}^{\infty} f(\tau) \delta(t – \tau) \, d\tau = f(t) $$

微分との関係

$$ \frac{d}{dt}(f * g) = \frac{df}{dt} * g = f * \frac{dg}{dt} $$

畳み込み定理

畳み込みの最も重要な性質は、フーリエ変換との関係です。

$\mathcal{F}\{f\}$ で $f$ のフーリエ変換を表すと、

$$ \mathcal{F}\{f * g\} = \mathcal{F}\{f\} \cdot \mathcal{F}\{g\} $$

すなわち、時間領域での畳み込みは周波数領域での積に対応します。

逆に、時間領域での積は周波数領域での畳み込みに対応します。

$$ \mathcal{F}\{f \cdot g\} = \mathcal{F}\{f\} * \mathcal{F}\{g\} $$

畳み込み定理の重要性

畳み込みを直接計算すると、離散の場合 $O(N^2)$ の計算量がかかります。しかし、畳み込み定理を利用すれば、

  1. FFTでフーリエ変換: $O(N \log N)$
  2. 周波数領域で要素ごとの積: $O(N)$
  3. 逆FFTで時間領域に戻す: $O(N \log N)$

で計算でき、合計 $O(N \log N)$ に高速化されます。

信号処理での応用

線形時不変システム(LTIシステム)

信号処理において、線形時不変(LTI)システムの出力 $y(t)$ は、入力信号 $x(t)$ とインパルス応答 $h(t)$ の畳み込みで表されます。

$$ y(t) = (x * h)(t) = \int_{-\infty}^{\infty} x(\tau) h(t – \tau) \, d\tau $$

インパルス応答 $h(t)$ がシステムの特性を完全に記述しており、任意の入力に対する出力を畳み込みで求められます。

移動平均フィルタ

最も単純なフィルタの例として、移動平均があります。窓幅 $T$ の矩形パルスとの畳み込みは、区間 $T$ にわたる移動平均に対応します。

Pythonでの実装

畳み込みの計算と畳み込み定理の検証を行います。

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal as sig

# 2つの関数の畳み込みの可視化
t = np.linspace(-5, 10, 1000)
dt = t[1] - t[0]

# 矩形パルス
def rect(t, width=1):
    return np.where(np.abs(t) <= width/2, 1, 0)

# 信号: 矩形パルス
f = rect(t - 1, width=2)  # [0, 2]の矩形パルス
g = np.exp(-t) * (t >= 0)  # 指数減衰(因果的)

# 畳み込みの計算
conv_result = np.convolve(f, g, mode='full') * dt
t_conv = np.arange(len(conv_result)) * dt + 2 * t[0]

fig, axes = plt.subplots(3, 1, figsize=(10, 8))

axes[0].plot(t, f, 'b-', linewidth=2)
axes[0].set_title('$f(t)$: Rectangle pulse')
axes[0].set_ylabel('Amplitude')
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(-5, 10)

axes[1].plot(t, g, 'r-', linewidth=2)
axes[1].set_title('$g(t)$: Exponential decay')
axes[1].set_ylabel('Amplitude')
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim(-5, 10)

axes[2].plot(t_conv, conv_result, 'g-', linewidth=2)
axes[2].set_title('$(f * g)(t)$: Convolution')
axes[2].set_xlabel('t')
axes[2].set_ylabel('Amplitude')
axes[2].grid(True, alpha=0.3)
axes[2].set_xlim(-5, 10)

plt.tight_layout()
plt.show()

# 畳み込み定理の検証
N = 1024
t2 = np.linspace(0, 10, N)
dt2 = t2[1] - t2[0]

# 2つの信号
f2 = np.sin(2 * np.pi * 1 * t2) + 0.5 * np.sin(2 * np.pi * 3 * t2)
g2 = np.exp(-2 * t2)

# 方法1: 時間領域での畳み込み
conv_time = np.convolve(f2, g2, mode='full')[:N] * dt2

# 方法2: 周波数領域での積(畳み込み定理)
F = np.fft.fft(f2)
G = np.fft.fft(g2)
conv_freq = np.real(np.fft.ifft(F * G)) * dt2 * N / len(f2)

# 比較
fig, axes = plt.subplots(2, 1, figsize=(10, 6))

axes[0].plot(t2, conv_time, 'b-', linewidth=2, label='Time domain convolution')
axes[0].plot(t2, conv_freq, 'r--', linewidth=2, label='FFT-based (Convolution Theorem)')
axes[0].set_xlabel('t')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Convolution Theorem Verification')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# ローパスフィルタの例
freq = np.linspace(0, 1/(2*dt2), N//2)
axes[1].plot(freq, 2/N * np.abs(F[:N//2]), 'b-', label='|F(f)|')
axes[1].plot(freq, 2/N * np.abs(G[:N//2]), 'r-', label='|G(f)|')
axes[1].plot(freq, 2/N * np.abs(F[:N//2] * G[:N//2]), 'g-', label='|F(f) G(f)|')
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Magnitude')
axes[1].set_title('Frequency Domain')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim(0, 10)

plt.tight_layout()
plt.show()

上段のグラフでは、矩形パルスと指数減衰関数の畳み込みを時間領域で直接計算しています。下段では、畳み込み定理により時間領域の畳み込みとFFTベースの計算が一致することを確認しています。

まとめ

本記事では、畳み込み積分について解説しました。

  • 畳み込み積分は $(f * g)(t) = \int f(\tau)g(t-\tau)d\tau$ で定義され、「関数をスライドさせながら重なり面積を計算する」操作に対応する
  • 可換律・結合律・分配律が成り立ち、デルタ関数が単位元として機能する
  • 畳み込み定理により、時間領域の畳み込みは周波数領域の積に対応し、FFTを使って $O(N \log N)$ で高速に計算できる
  • LTIシステムでは、出力は入力とインパルス応答の畳み込みで完全に記述される