Skip to content

Commit 22745c0

Browse files
committed
feat: Add Fast Fourier Transform (FFT) implementation and convolution functions
1 parent 8154f85 commit 22745c0

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/multiplication_of_big_integers"
2+
3+
#include "../weilycoder/poly/fft_convolve.hpp"
4+
#include <algorithm>
5+
#include <iostream>
6+
#include <string>
7+
#include <vector>
8+
using namespace std;
9+
using namespace weilycoder;
10+
11+
static void solve() {
12+
string a, b;
13+
cin >> a >> b;
14+
15+
reverse(a.begin(), a.end()), reverse(b.begin(), b.end());
16+
17+
bool negative = false;
18+
if (a.back() == '-')
19+
negative = !negative, a.pop_back();
20+
if (b.back() == '-')
21+
negative = !negative, b.pop_back();
22+
23+
vector<double> A, B;
24+
for (char c : a)
25+
A.push_back(c - '0');
26+
for (char c : b)
27+
B.push_back(c - '0');
28+
29+
vector<double> C = fft_convolve_real(A, B);
30+
vector<int> result;
31+
int carry = 0;
32+
for (double x : C) {
33+
int val = static_cast<int>(x + 0.5) + carry;
34+
result.push_back(val % 10);
35+
carry = val / 10;
36+
}
37+
while (carry) {
38+
result.push_back(carry % 10);
39+
carry /= 10;
40+
}
41+
while (!result.empty() && result.back() == 0)
42+
result.pop_back();
43+
if (result.empty()) {
44+
cout << 0 << '\n';
45+
} else {
46+
if (negative)
47+
cout << '-';
48+
for (auto it = result.rbegin(); it != result.rend(); ++it)
49+
cout << *it;
50+
cout << '\n';
51+
}
52+
}
53+
54+
int main() {
55+
cin.tie(nullptr)->sync_with_stdio(false);
56+
cin.exceptions(cin.failbit | cin.badbit);
57+
size_t t;
58+
cin >> t;
59+
while (t--)
60+
solve();
61+
return 0;
62+
}

weilycoder/poly/fft.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef WEILYCODER_POLY_FFT_HPP
2+
#define WEILYCODER_POLY_FFT_HPP
3+
4+
#include "fft_utility.hpp"
5+
#include <complex>
6+
#include <cstddef>
7+
#include <vector>
8+
9+
/**
10+
* @file fft.hpp
11+
* @brief Implementation of Fast Fourier Transform (FFT) and its inverse.
12+
*/
13+
14+
namespace weilycoder {
15+
/**
16+
* @brief In-place iterative Cooley–Tukey FFT / inverse FFT on a complex vector.
17+
* The length of the input vector should be a power of two.
18+
* @tparam on Direction of the transform: 1 for FFT, -1 for inverse FFT.
19+
* @tparam float_t Floating-point type for computations (default: double).
20+
* @param y Vector of complex numbers to be transformed in-place.
21+
*/
22+
template <int32_t on = 1, typename float_t = double>
23+
void fft(std::vector<std::complex<float_t>> &y) {
24+
static_assert(on == 1 || on == -1, "on must be 1 or -1");
25+
fft_change(y);
26+
for (size_t h = 2; h <= y.size(); h <<= 1) {
27+
std::complex<float_t> wn(cos(2 * PI<float_t> / h), sin(on * 2 * PI<float_t> / h));
28+
for (size_t j = 0; j < y.size(); j += h) {
29+
std::complex<float_t> w(1, 0);
30+
for (size_t k = j; k < j + (h >> 1); ++k, w *= wn) {
31+
std::complex<float_t> u = y[k], t = w * y[k + (h >> 1)];
32+
y[k] = u + t, y[k + (h >> 1)] = u - t;
33+
}
34+
}
35+
}
36+
size_t len = y.size();
37+
if constexpr (on == -1)
38+
for (size_t i = 0; i < len; ++i)
39+
y[i] /= len;
40+
}
41+
} // namespace weilycoder
42+
43+
#endif

weilycoder/poly/fft_convolve.hpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#ifndef WEILYCODER_POLY_FFT_CONVOLVE_HPP
2+
#define WEILYCODER_POLY_FFT_CONVOLVE_HPP
3+
4+
#include "fft.hpp"
5+
#include <complex>
6+
#include <cstddef>
7+
#include <vector>
8+
9+
/**
10+
* @file fft_convolve.hpp
11+
* @brief Functions for performing convolution using Fast Fourier Transform (FFT).
12+
*/
13+
14+
namespace weilycoder {
15+
/**
16+
* @brief Perform convolution of two complex vectors using FFT.
17+
* @tparam float_t Floating-point type for computations (default: double).
18+
* @param a First input vector of complex numbers.
19+
* @param b Second input vector of complex numbers.
20+
* @return Vector containing the convolution result.
21+
*/
22+
template <typename float_t = double>
23+
std::vector<std::complex<float_t>> fft_convolve(std::vector<std::complex<float_t>> a,
24+
std::vector<std::complex<float_t>> b) {
25+
size_t n = 1;
26+
while (n < a.size() + b.size() - 1)
27+
n <<= 1;
28+
a.resize(n), b.resize(n);
29+
fft(a), fft(b);
30+
for (size_t i = 0; i < n; ++i)
31+
a[i] *= b[i];
32+
return fft<-1>(a), a;
33+
}
34+
35+
/**
36+
* @brief Perform convolution of two real-valued vectors using FFT.
37+
* @tparam float_t Floating-point type for computations (default: double).
38+
* @param a First input vector of real numbers.
39+
* @param b Second input vector of real numbers.
40+
* @return Vector containing the convolution result.
41+
* @note This function uses a technique that combines two real sequences into
42+
* a single complex sequence to perform the convolution more efficiently.
43+
*/
44+
template <typename float_t = double>
45+
std::vector<float_t> fft_convolve_real(const std::vector<float_t> &a,
46+
const std::vector<float_t> &b) {
47+
size_t n = 1;
48+
while (n < a.size() + b.size() - 1)
49+
n <<= 1;
50+
std::vector<std::complex<float_t>> F(n);
51+
for (size_t i = 0; i < a.size(); ++i)
52+
F[i].real(a[i]);
53+
for (size_t i = 0; i < b.size(); ++i)
54+
F[i].imag(b[i]);
55+
fft(F);
56+
for (size_t i = 0; i < n; ++i)
57+
F[i] *= F[i];
58+
fft<-1>(F);
59+
std::vector<float_t> result(a.size() + b.size() - 1);
60+
for (size_t i = 0; i < result.size(); ++i)
61+
result[i] = F[i].imag() / 2;
62+
return result;
63+
}
64+
} // namespace weilycoder
65+
66+
#endif

weilycoder/poly/fft_utility.hpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef WEILYCODER_POLY_FFT_UTILITY_HPP
2+
#define WEILYCODER_POLY_FFT_UTILITY_HPP
3+
4+
#include <complex>
5+
#include <cstddef>
6+
#include <vector>
7+
8+
/**
9+
* @file fft_utility.hpp
10+
* @brief Utility functions and constants for Fast Fourier Transform (FFT)
11+
*/
12+
13+
namespace weilycoder {
14+
/**
15+
* @brief Alias for the commonly used complex number type with double precision.
16+
*/
17+
using complex_t = std::complex<double>;
18+
19+
/**
20+
* @brief Compile-time constant for π (pi) parameterized by numeric type.
21+
* @tparam T Numeric type (e.g., float, double, long double).
22+
*/
23+
template <typename T> constexpr T PI = T(3.1415926535897932384626433832795l);
24+
25+
/**
26+
* @brief Perform in-place bit-reversal permutation (index reordering) required by
27+
* iterative FFT.
28+
* @tparam T Element type stored in the vector. Must be Swappable and efficiently
29+
* copyable.
30+
* @param a Vector to be permuted in-place. Its size should typically be a power
31+
* of two for use with the FFT implementation in this file.
32+
*/
33+
template <typename T> void fft_change(std::vector<T> &a) {
34+
size_t n = a.size();
35+
std::vector<size_t> rev(n);
36+
for (size_t i = 0; i < n; ++i) {
37+
rev[i] = rev[i >> 1] >> 1;
38+
if (i & 1)
39+
rev[i] |= n >> 1;
40+
if (i < rev[i])
41+
swap(a[i], a[rev[i]]);
42+
}
43+
}
44+
} // namespace weilycoder
45+
46+
#endif

0 commit comments

Comments
 (0)