-
Notifications
You must be signed in to change notification settings - Fork 775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added a FFTPolynomial file #87
base: main
Are you sure you want to change the base?
Changes from 41 commits
6011aa5
ba0cc27
bba7a32
a2b9413
e83e65b
5b5a4f4
12f0a0a
75ba4da
81e2ae8
77adbd4
5243182
ba5bc7d
ace08ab
7d26dee
7c16aeb
c77b038
fe7b393
64ef0ab
f5ecd33
b7eb861
f072eb1
d5664e4
2ea261d
cdd8367
99f761d
b984354
b230f10
6f5b435
8bd2091
3fe590e
5ea7caa
4c549b9
e3ff553
936726e
4b4f443
14cb23e
6148b2d
3ca9b81
92095f1
f277725
02e9270
120f099
094e5d5
a4616a9
640a997
ccb400b
e165e20
464bfe7
613267c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,31 @@ | |
* Date: 2019-01-09 | ||
* License: CC0 | ||
* Source: http://neerc.ifmo.ru/trains/toulouse/2017/fft2.pdf (do read, it's excellent) | ||
Papers about accuracy: http://www.daemonology.net/papers/fft.pdf, http://www.cs.berkeley.edu/~fateman/papers/fftvsothers.pdf | ||
For integers rounding works if $(|a| + |b|)\max(a, b) < \mathtt{\sim} 10^9$, or in theory maybe $10^6$. | ||
Accuracy bound from http://www.daemonology.net/papers/fft.pdf | ||
* Description: fft(a, ...) computes $\hat f(k) = \sum_x a[x] \exp(2\pi i \cdot k x / N)$ for all $k$. Useful for convolution: | ||
\texttt{conv(a, b) = c}, where $c[x] = \sum a[i]b[x-i]$. | ||
For convolution of complex numbers or more than two vectors: FFT, multiply | ||
pointwise, divide by n, reverse(start+1, end), FFT back. | ||
For integers, consider using a number-theoretic transform instead, to avoid rounding issues. | ||
Let N be $\max(|a|,|b|)$. Is guaranteed safe as long as $N\log_2{N}\max(a)\max(b) < \mathtt{\sim} 10^{16}$ . | ||
Consider using number-theoretic transform or FFTMod instead if precision is an issue. | ||
* Time: O(N \log N), where $N = |A|+|B|-1$ ($\tilde 1s$ for $N=2^{22}$) | ||
* Status: somewhat tested | ||
*/ | ||
#pragma once | ||
|
||
typedef complex<double> C; | ||
typedef complex<long double> Cd; | ||
typedef vector<double> vd; | ||
|
||
void fft(vector<C> &a, vector<C> &rt, vi& rev, int n) { | ||
void fft(vector<C> &a, int n, int L, vector<C> &rt) { | ||
vi rev(n); | ||
rep(i,0,n) rev[i] = (rev[i / 2] | (i & 1) << L) / 2; | ||
if (rt.empty()) { | ||
rt.assign(n, 1); | ||
for (int k = 2; k < n; k *= 2) { | ||
Cd z[] = {1, polar(1.0, M_PI / k)}; | ||
rep(i, k, 2 * k) rt[i] = Cd(rt[i / 2]) * z[i & 1]; | ||
} | ||
} | ||
rep(i,0,n) if (i < rev[i]) swap(a[i], a[rev[i]]); | ||
for (int k = 1; k < n; k *= 2) | ||
for (int i = 0; i < n; i += 2 * k) rep(j,0,k) { | ||
|
@@ -27,25 +36,19 @@ void fft(vector<C> &a, vector<C> &rt, vi& rev, int n) { | |
C z(x[0]*y[0] - x[1]*y[1], x[0]*y[1] + x[1]*y[0]); /// exclude-line | ||
a[i + j + k] = a[i + j] - z; | ||
a[i + j] += z; | ||
} | ||
} | ||
} | ||
|
||
vd conv(const vd& a, const vd& b) { | ||
vd conv(const vd &a, const vd &b) { | ||
if (a.empty() || b.empty()) return {}; | ||
vd res(sz(a) + sz(b) - 1); | ||
int L = 32 - __builtin_clz(sz(res)), n = 1 << L; | ||
vector<C> in(n), out(n), rt(n, 1); vi rev(n); | ||
rep(i,0,n) rev[i] = (rev[i/2] | (i&1) << L) / 2; | ||
for (int k = 2; k < n; k *= 2) { | ||
C z[] = {1, polar(1.0, M_PI / k)}; | ||
rep(i,k,2*k) rt[i] = rt[i/2] * z[i&1]; | ||
} | ||
vector<C> in(n), out(n), rt; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe make these static if you want to reuse the memory? |
||
copy(all(a), begin(in)); | ||
rep(i,0,sz(b)) in[i].imag(b[i]); | ||
fft(in, rt, rev, n); | ||
fft(in, n, L, rt); | ||
trav(x, in) x *= x; | ||
rep(i,0,n) out[i] = in[-i & (n - 1)] - conj(in[i]); | ||
fft(out, rt, rev, n); | ||
rep(i,0,sz(res)) res[i] = imag(out[i]) / (4*n); | ||
fft(out, n, L, rt); | ||
rep(i,0,sz(res)) res[i] = imag(out[i]) / (4 * n); | ||
return res; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (need to merge with master to get rid of these fft changes from the diff) |
||
* Author: chilli | ||
* Date: 2019-04-25 | ||
* License: CC0 | ||
* Source: http://neerc.ifmo.ru/trains/toulouse/2017/fft2.pdf | ||
* Description: Higher precision FFT, can be used for convolutions modulo arbitrary integers. | ||
* Let N be $\max(|a|,|b|)$. Is guaranteed safe as long as $N\log_2{N}\sqrt{\max(a)\max(b)} < \mathtt{\sim} 10^{16}$ . | ||
* Time: O(N \log N), where $N = |A|+|B|-1$ (twice as slow as NTT or FFT) | ||
* Status: somewhat tested | ||
*/ | ||
#pragma once | ||
|
||
#include "FastFourierTransform.h" | ||
|
||
typedef vector<ll> vl; | ||
template <int M> vl convMod(const vl &a, const vl &b) { | ||
if (a.empty() || b.empty()) return {}; | ||
vl res(sz(a) + sz(b) - 1); | ||
int B=32-__builtin_clz(sz(res)), n = 1<<B, cut=int(sqrt(M)); | ||
vector<C> L(n), R(n), outs(n), outl(n), rt; | ||
rep(i,0,sz(a)) L[i] = Cd(a[i] / cut, a[i] % cut); | ||
rep(i,0,sz(b)) R[i] = Cd(b[i] / cut, b[i] % cut); | ||
fft(L, n, B, rt), fft(R, n, B, rt); | ||
rep(i,0,n) { | ||
int j = -i & (n - 1); | ||
outl[j] = (L[i] + conj(L[j])) * R[i] / (2.0 * n); | ||
outs[j] = (L[i] - conj(L[j])) * R[i] / (2.0 * n) / 1i; | ||
} | ||
fft(outl, n, B, rt), fft(outs, n, B, rt); | ||
rep(i,0,sz(res)) { | ||
ll av = ll(outl[i].real()+.5), cv = ll(outs[i].imag()+.5); | ||
ll bv = ll(outl[i].imag()+.5) + ll(outs[i].real()+.5); | ||
res[i] = ((av % M * cut + bv % M) * cut + cv % M) % M; | ||
} | ||
return res; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* Author: chilli, Andrew He, Adamant | ||
* Date: 2019-04-27 | ||
* Description: A FFT based Polynomial class. | ||
*/ | ||
#pragma once | ||
|
||
#include "../number-theory/ModularArithmetic.h" | ||
#include "FastFourierTransform.h" | ||
#include "FastFourierTransformMod.h" | ||
// #include "NumberTheoreticTransform.h" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: NTT will be the common case, if that matters to you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is currently commented out for a stupid reason - |
||
|
||
typedef Mod num; | ||
typedef vector<num> poly; | ||
vector<Mod> conv(vector<Mod> a, vector<Mod> b) { | ||
auto res = convMod<mod>(vl(all(a)), vl(all(b))); | ||
// auto res = conv(vl(all(a)), vl(all(b))); | ||
return vector<Mod>(all(res)); | ||
} | ||
Chillee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
poly &operator+=(poly &a, const poly &b) { | ||
a.resize(max(sz(a), sz(b))); | ||
rep(i, 0, sz(b)) a[i] = a[i] + b[i]; | ||
return a; | ||
} | ||
poly &operator-=(poly &a, const poly &b) { | ||
a.resize(max(sz(a), sz(b))); | ||
rep(i, 0, sz(b)) a[i] = a[i] - b[i]; | ||
return a; | ||
} | ||
|
||
poly &operator*=(poly &a, const poly &b) { | ||
if (sz(a) + sz(b) < 100){ | ||
poly res(sz(a) + sz(b) - 1); | ||
rep(i,0,sz(a)) rep(j,0,sz(b)) | ||
res[i + j] = (res[i + j] + a[i] * b[j]); | ||
return (a = res); | ||
} | ||
return a = conv(a, b); | ||
} | ||
poly operator*(poly a, const num b) { | ||
poly c = a; | ||
trav(i, c) i = i * b; | ||
return c; | ||
} | ||
#define OP(o, oe) \ | ||
poly operator o(poly a, poly b) { \ | ||
poly c = a; \ | ||
return c oe b; \ | ||
} | ||
OP(*, *=) OP(+, +=) OP(-, -=); | ||
Chillee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
poly modK(poly a, int k) { return {a.begin(), a.begin() + min(k, sz(a))}; } | ||
poly inverse(poly A) { | ||
poly B = poly({num(1) / A[0]}); | ||
while (sz(B) < sz(A)) | ||
B = modK(B * (poly({num(2)}) - modK(A, 2*sz(B)) * B), 2 * sz(B)); | ||
return modK(B, sz(A)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#pragma once | ||
|
||
#include "PolynomialMod.h" | ||
|
||
vector<num> eval(const poly &a, const vector<num> &x) { | ||
int n = sz(x); | ||
if (!n) return {}; | ||
vector<poly> up(2 * n); | ||
rep(i, 0, n) up[i + n] = poly({num(0) - x[i], 1}); | ||
for (int i = n - 1; i > 0; i--) | ||
up[i] = up[2 * i] * up[2 * i + 1]; | ||
vector<poly> down(2 * n); | ||
down[1] = a % up[1]; | ||
rep(i, 2, 2 * n) down[i] = down[i / 2] % up[i]; | ||
vector<num> y(n); | ||
rep(i, 0, n) y[i] = down[i + n][0]; | ||
return y; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include "PolynomialMod.h" | ||
#include "PolynomialPow.h" | ||
#include "PolynomialEval.h" | ||
|
||
poly interp(vector<num> x, vector<num> y) { | ||
int n=sz(x); | ||
vector<poly> up(n*2); | ||
rep(i,0,n) up[i+n] = poly({num(0)-x[i], num(1)}); | ||
for(int i=n-1; i>0;i--) up[i] = up[2*i]*up[2*i+1]; | ||
vector<num> a = eval(deriv(up[1]), x); | ||
vector<poly> down(2*n); | ||
rep(i,0,n) down[i+n] = poly({y[i]*(num(1)/a[i])}); | ||
for(int i=n-1;i>0;i--) down[i] = down[i*2] * up[i*2+1] + down[i*2+1] * up[i*2]; | ||
return down[1]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#pragma once | ||
|
||
#include "PolynomialBase.h" | ||
|
||
poly &operator/=(poly &a, poly b) { | ||
if (sz(a) < sz(b)) | ||
return a = {}; | ||
int s = sz(a) - sz(b) + 1; | ||
reverse(all(a)), reverse(all(b)); | ||
a.resize(s), b.resize(s); | ||
a = a * inverse(b); | ||
a.resize(s), reverse(all(a)); | ||
return a; | ||
} | ||
OP(/, /=) | ||
poly &operator%=(poly &a, poly &b) { | ||
if (sz(a) < sz(b)) | ||
return a; | ||
poly c = (a / b) * b; | ||
a.resize(sz(b) - 1); | ||
rep(i, 0, sz(a)) a[i] = a[i] - c[i]; | ||
return a; | ||
} | ||
OP(%, %=) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#pragma once | ||
|
||
#include "PolynomialBase.h" | ||
poly deriv(poly a) { | ||
if (a.empty()) | ||
return {}; | ||
poly b(sz(a) - 1); | ||
rep(i, 1, sz(a)) b[i - 1] = a[i] * num(i); | ||
return b; | ||
} | ||
poly integr(poly a) { | ||
if (a.empty()) return {0}; | ||
poly b(sz(a) + 1); | ||
b[1] = num(1); | ||
rep(i, 2, sz(b)) b[i] = b[mod%i]*Mod(-mod/i+mod); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't work for complex, probably worth noting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it even worth noting stuff that doesn't work with complex numbers? Or is it reasonable to assume that every problem involving polynomials/series will be done on a finite field? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idk, the field of reals seems like it could be useful sometimes. |
||
rep(i, 1 ,sz(b)) b[i] = a[i-1] * b[i]; | ||
return b; | ||
} | ||
poly log(poly a) { | ||
return modK(integr(deriv(a) * inverse(a)), sz(a)); | ||
} | ||
poly exp(poly a) { | ||
poly b(1, num(1)); | ||
if (a.empty()) | ||
return b; | ||
while (sz(b) < sz(a)) { | ||
b.resize(sz(b) * 2); | ||
b *= (poly({num(1)}) + modK(a, sz(b)) - log(b)); | ||
b.resize(sz(b) / 2 + 1); | ||
} | ||
return modK(b, sz(a)); | ||
} | ||
poly pow(poly a, ll m) { | ||
int p = 0, n = sz(a); | ||
while (p < sz(a) && a[p].x == 0) | ||
++p; | ||
if (ll(m)*p >= sz(a)) return poly(sz(a)); | ||
num j = a[p]; | ||
a = {a.begin() + p, a.end()}; | ||
a = a * (num(1) / j); | ||
a.resize(n); | ||
auto res = exp(log(a) * num(m)) * (j ^ m); | ||
res.insert(res.begin(), p*m, 0); | ||
return modK(res, n); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can merge this loop with the generating loop if you want, would keep things more compact and (marginally) improve cache locality.