LoginSignup
3
4

More than 3 years have passed since last update.

【Python】ワンライナーFFT(高速フーリエ変換)などという狂気

Last updated at Posted at 2020-08-05

使うNumpy関数+定数

from numpy import tile, interp, linspace, exp, r_, pi

DFT(遅い)

def dft(x):return exp(-2j * pi * r_[:len(x)] / len(x))**r_[[r_[:len(x)]]].T @ x

FFT(2の累乗要素限定)

def fft(x):return tile(fft(x[::2]), 2) + (r_[[[1],[-1]]] * (exp(-2j * pi * r_[:len(x) // 2] / len(x)) * fft(x[1::2]))).ravel() if len(x) > 1 else x

FFT(要素数制限なし、長い)

def fft(x):return interp(linspace(0, 1 << (len(f'{len(x) - 1:b}')), len(x)), r_[:1 << (len(f'{len(x) - 1:b}'))], fft(r_[x, [0] * ((1 << (len(f'{len(x) - 1:b}'))) - len(x))])) if ('1' in f'{len(x):b}' [1:]) else tile(fft(x[::2]), 2) + (r_[[[1], [-1]]] * (exp(-2j * pi * r_[:len(x) // 2] / len(x)) * fft(x[1::2]))).ravel() if len(x) > 1 else x

動作確認

from numpy import sin, fft, random
import matplotlib.pyplot as plt

変換する波形

周波数、振幅がランダムな5つの$sin$波の合成、$2048(=2^{11})$要素

n = 1 << 11
x = linspace(0, 2 * pi, n)
y = 0

for w in n / 2 * random.rand(5):
    y += random.rand() * sin(w * x)

plt.plot(x, y, lw = .3)

image.png

Numpy.fft.fft

%timeit fft.fft(y)
plt.plot(abs(fft.fft(y))**2)

9.01 µs ± 42.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
image.png

DFT

%timeit dft(y)
plt.plot(abs(dft(y))**2)

568 ms ± 899 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
image.png

FFT(2の累乗要素限定のほう)

%timeit fft(y)
plt.plot(abs(fft(y))**2)

51.2 ms ± 76.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
image.png

FFT(長いほう)

%timeit fft(y)
plt.plot(abs(fft(y))**2)

54.1 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
image.png

まとめ

NumpyのFFT早すぎィ!
出力は一致してますが、FFTではただのDFTの約10倍高速化出来ていることが確認できます。

3
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4