使う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)
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)
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)
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)
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)
#まとめ
NumpyのFFT早すぎィ!
出力は一致してますが、FFTではただのDFTの約10倍高速化出来ていることが確認できます。