Newton's Method 是牛顿提出的一种将非线性方程线性化的近似方法。它也可以运用在多项式中,求关于多项式的非线性方程在模意义下的解。
要学习 Newton's Method,得先了解泰勒级数。
泰勒级数和麦克劳林级数
泰勒级数用无限项连加式来表示函数。一般地,对于一个光滑函数
泰勒展开式并非对于
一般的 Newton's Method
Newton's Method 一般被用于求解非线性方程。它是这样求
- 选取合适一个数作为
- 将
在 处展开,即 - 取其常数项和线性项的系数,令其值为
,即 - 解得这个近似方程的根
,并在 处将 泰勒展开,重复上述过程得到 可以证明,每一个新解都更加接近 的根
double f(double x);
double fd(double x);//fd 是 f 的导数,即 f'
double newtonMethod(double x0, int d){ //d 代表迭代次数
//f(x_0)+fd(x0)*(x-x0)=0 -> x=-f(x0)/fd(x0)+x0
while(d--) x0 = -f(x0) / fd(x0) + x0;
return x0;
}
这样看来,我们只能无限逼近
Newton's Method 在多项式中的 Methodology
求一个关于
的多项式 ,使得对于一给定的关于 的函数 , 成立。
这是 Newton's Method 求解的问题的通式。对于多项式
选择一个满足
设我们已经求出了模
将
即
其中,对于
解得
在
有一个问题尚未解决:
由上述过程可知,Newton's Method 可应用于解
下面将就几个具体的
Newton's Method 求多项式的逆
对于原多项式
注意这里
即
运用 FNTT,单次迭代复杂度为
void polyinv(int *f, const int *h, int n){
int N = 1; while(N < n + n - 1) N <<= 1;
static int d[maxn], g[maxn];
memcpy(d, h, n * sizeof(int)), memset(d + n, 0, (N - n) * sizeof(int));
memset(f, 0, N * sizeof(int)), memset(g, 0, N * sizeof(int)), f[0] = qpow(h[0], mod - 2);
for(int w = 2; w / 2 < n; w <<= 1){
memcpy(g, d, w * sizeof(int));
for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
dft(f, w << 1, 1), dft(g, w << 1, 1);
for(int i = 0; i < w * 2; ++i) f[i] = (ll)f[i] * (2 - (ll)f[i] * g[i] % mod) % mod;
dft(f, w << 1, -1);
for(int i = 0, inv = qpow(w << 1, mod - 2); i < w; ++i) f[i] = (ll)f[i] * inv % mod;
memset(f + w, 0, w * sizeof(int));
}
memset(f + n, 0, (N - n) * sizeof(int));
}
Newton's Method 求多项式平方根
对于多项式
单次迭代仍为
void polysqrt(int *f, const int *h, int n){
int N = 1; while(N < n + n - 1) N <<= 1;
static int d[maxn], g[maxn], f_inv[maxn];
memcpy(d, h, n * sizeof(int)), memset(d + n, 0, (N - n) * sizeof(int));
memset(f, 0, N * sizeof(int)), memset(g, 0, N * sizeof(int)), f[0] = 1;
for(int w = 2; w / 2 < n; w <<= 1){
memcpy(g, d, w * sizeof(int)), polyinv(f_inv, f, w);
for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
dft(g, w << 1, 1), dft(f, w << 1, 1), dft(f_inv, w << 1, 1);
for(int i = 0; i < w * 2; ++i) f[i] = (f[i] + (ll)f_inv[i] * g[i] % mod) % mod;
dft(f, w << 1, -1);
for(int i = 0, inv = qpow(w << 2, mod - 2); i < w; ++i) f[i] = (ll)f[i] * inv % mod;
memset(f + w, 0, w * sizeof(int));
}
memset(f + n, 0, (N - n) * sizeof(int));
}
Newton's Method 求多项式指数函数
多项式的对数函数和指数函数都是用麦克劳林级数定义的。要用 Newton's Method 求多项式指数函数,得先会求多项式对数函数。
对于多项式
单次迭代仍为
void polyexp(int *f, const int *h, int n){
int N = 1; while(N < n + n - 1) N <<= 1;
static int d[maxn], g[maxn], lg[maxn]; memset(lg, 0, N * sizeof(int));
memcpy(d, h, n * sizeof(int)), memset(d + n, 0, (N - n) * sizeof(int));
memset(g, 0, N * sizeof(int)), memset(f, 0, N * sizeof(int)), f[0] = 1;
for(int w = 2; w / 2 < n; w <<= 1){
memcpy(g, d, w * sizeof(int)), polylog(lg, f, w);
for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
dft(g, w << 1, 1), dft(f, w << 1, 1), dft(lg, w << 1, 1);
for(int i = 0; i < w * 2; ++i) f[i] = (ll)f[i] * (1 + g[i] - lg[i]) % mod;
dft(f, w << 1, -1);
for(int i = 0, inv = qpow(w << 1, mod - 2); i < w; ++i) f[i] = (ll)f[i] * inv % mod;
memset(f + w, 0, w * sizeof(int));
}
memset(f + n, 0, (N - n) * sizeof(int));
}
一个有趣的事实是,要写多项式指数函数就必须先写一个多项式对数函数;要写多项式对数函数就必须先写一个多项式求逆......这些东西加起来有七八十行,说实话还是不太好写的。