algorithm-dev

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub today2098/algorithm-dev

:heavy_check_mark: Discrete Fourier Transform(離散フーリエ変換)
(algorithm/Math/Convolution/discrete_fourier_transform.hpp)

概要

離散フーリエ変換 (DFT: Discrete Fourier Transform) を用いた畳み込みを行う.

具体的には,長さ $N$ の数列 $\lbrace a_0, a_1, \ldots, a_{N-1} \rbrace$ と長さ $M$ の数列 $\lbrace b_0, b_1, \ldots, b_{M-1} \rbrace$ に対して,

\[c_i = \sum_{k=0}^{i} a_k b_{i-k}\]

となる長さ $N + M - 1$ の数列 $\lbrace c_0, c_1, \ldots, c_{N+M-1} \rbrace$ を求める. 計算量は $\mathcal{O}((N + M)^2)$ .

数列の長さや要素の値が大きくなると,誤差も大きくなることに注意.

参考文献

  1. “離散フーリエ変換”. Wikipedia. https://ja.wikipedia.org/wiki/離散フーリエ変換.
  2. AngrySadEight. “高速フーリエ変換・数論変換を改めて理解しようという話”. Qiita. https://qiita.com/AngrySadEight/items/0dfde26060daaf6a2fda.
  3. tatyam. “【競プロer向け】FFT を習得しよう!”. 東京工業大学で活動するデジタル創作. https://trap.jp/post/1386/.
  4. “競プロのための高速フーリエ変換”. https://www.creativ.xyz/fast-fourier-transform/.

Verified with

Code

#ifndef ALGORITHM_DISCRETE_FOURIER_TRANSFORM_HPP
#define ALGORITHM_DISCRETE_FOURIER_TRANSFORM_HPP 1

/**
 * @brief Discrete Fourier Transform(離散フーリエ変換)
 * @docs docs/Math/Convolution/discrete_fourier_transform.md
 */

#include <algorithm>
#include <cmath>
#include <complex>
#include <type_traits>
#include <vector>

namespace algorithm {

namespace dft {

using D = double;

const D PI = std::acos(-1.0);

// Discrete Fourier Transform(離散フーリエ変換). O(N^2).
void transform(std::vector<std::complex<D> > &a, bool inv = false) {
    if(a.size() == 0) return;
    const int n = a.size();
    std::vector<std::complex<D> > res(n, 0.0);
    D ang = 2 * PI / n;
    if(inv) ang = -ang;
    for(int i = 0; i < n; ++i) {
        D tmp = ang * i;
        for(int j = 0; j < n; ++j) res[i] += a[j] * std::polar<D>(1.0, tmp * j);
    }
    if(inv) {
        for(int i = 0; i < n; ++i) res[i] /= n;
    }
    a = res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type>
std::vector<Type> convolve_naive(const std::vector<Type> &a, const std::vector<Type> &b) {
    const int n = a.size(), m = b.size();
    if(n == 0 or m == 0) return std::vector<Type>();
    std::vector<Type> res(n + m - 1, 0);
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) res[i + j] += a[i] * b[j];
    }
    return res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type, typename std::enable_if_t<std::is_integral_v<Type>, bool> = false>
std::vector<Type> convolve(const std::vector<Type> &a, const std::vector<Type> &b) {
    if(a.size() == 0 or b.size() == 0) return std::vector<Type>();
    const int n = a.size() + b.size() - 1;
    std::vector<std::complex<D> > na(n, 0.0), nb(n, 0.0);
    std::copy(a.begin(), a.end(), na.begin());
    std::copy(b.begin(), b.end(), nb.begin());
    transform(na), transform(nb);
    for(int i = 0; i < n; ++i) na[i] *= nb[i];
    transform(na, true);
    std::vector<Type> res(n);
    for(int i = 0; i < n; ++i) res[i] = na[i].real() + (na[i].real() < 0.0 ? -0.5 : 0.5);
    return res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type, typename std::enable_if_t<std::is_floating_point_v<Type>, bool> = false>
std::vector<Type> convolve(const std::vector<Type> &a, const std::vector<Type> &b) {
    if(a.size() == 0 or b.size() == 0) return std::vector<Type>();
    const int n = a.size() + b.size() - 1;
    std::vector<std::complex<D> > na(n, 0.0), nb(n, 0.0);
    std::copy(a.begin(), a.end(), na.begin());
    std::copy(b.begin(), b.end(), nb.begin());
    transform(na), transform(nb);
    for(int i = 0; i < n; ++i) na[i] *= nb[i];
    transform(na, true);
    std::vector<Type> res(n);
    for(int i = 0; i < n; ++i) res[i] = na[i].real();
    return res;
}

}  // namespace dft

}  // namespace algorithm

#endif
#line 1 "algorithm/Math/Convolution/discrete_fourier_transform.hpp"



/**
 * @brief Discrete Fourier Transform(離散フーリエ変換)
 * @docs docs/Math/Convolution/discrete_fourier_transform.md
 */

#include <algorithm>
#include <cmath>
#include <complex>
#include <type_traits>
#include <vector>

namespace algorithm {

namespace dft {

using D = double;

const D PI = std::acos(-1.0);

// Discrete Fourier Transform(離散フーリエ変換). O(N^2).
void transform(std::vector<std::complex<D> > &a, bool inv = false) {
    if(a.size() == 0) return;
    const int n = a.size();
    std::vector<std::complex<D> > res(n, 0.0);
    D ang = 2 * PI / n;
    if(inv) ang = -ang;
    for(int i = 0; i < n; ++i) {
        D tmp = ang * i;
        for(int j = 0; j < n; ++j) res[i] += a[j] * std::polar<D>(1.0, tmp * j);
    }
    if(inv) {
        for(int i = 0; i < n; ++i) res[i] /= n;
    }
    a = res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type>
std::vector<Type> convolve_naive(const std::vector<Type> &a, const std::vector<Type> &b) {
    const int n = a.size(), m = b.size();
    if(n == 0 or m == 0) return std::vector<Type>();
    std::vector<Type> res(n + m - 1, 0);
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) res[i + j] += a[i] * b[j];
    }
    return res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type, typename std::enable_if_t<std::is_integral_v<Type>, bool> = false>
std::vector<Type> convolve(const std::vector<Type> &a, const std::vector<Type> &b) {
    if(a.size() == 0 or b.size() == 0) return std::vector<Type>();
    const int n = a.size() + b.size() - 1;
    std::vector<std::complex<D> > na(n, 0.0), nb(n, 0.0);
    std::copy(a.begin(), a.end(), na.begin());
    std::copy(b.begin(), b.end(), nb.begin());
    transform(na), transform(nb);
    for(int i = 0; i < n; ++i) na[i] *= nb[i];
    transform(na, true);
    std::vector<Type> res(n);
    for(int i = 0; i < n; ++i) res[i] = na[i].real() + (na[i].real() < 0.0 ? -0.5 : 0.5);
    return res;
}

// 畳み込み.
// 数列a[], b[]に対して,c[i]=sum_{k=0}^{i} a[k]*b[i-k] となる数列c[]を求める.O(N^2).
template <typename Type, typename std::enable_if_t<std::is_floating_point_v<Type>, bool> = false>
std::vector<Type> convolve(const std::vector<Type> &a, const std::vector<Type> &b) {
    if(a.size() == 0 or b.size() == 0) return std::vector<Type>();
    const int n = a.size() + b.size() - 1;
    std::vector<std::complex<D> > na(n, 0.0), nb(n, 0.0);
    std::copy(a.begin(), a.end(), na.begin());
    std::copy(b.begin(), b.end(), nb.begin());
    transform(na), transform(nb);
    for(int i = 0; i < n; ++i) na[i] *= nb[i];
    transform(na, true);
    std::vector<Type> res(n);
    for(int i = 0; i < n; ++i) res[i] = na[i].real();
    return res;
}

}  // namespace dft

}  // namespace algorithm
Back to top page