FFT可以用来计算多项式乘法,但是复数的运算中含有大量的浮点数,精度较低。对于只有整数参与运算的多项式,有时,\(\text{NTT(Number-Theoretic Transform)}\)会是更好的选择。

若\(a,p\)互素,且\(p>1\),对于\(a^k \equiv 1 (\mod p)\)的最小的\(k\),称为\(a\)模\(p\)的,记做\(\sigma_p(a)\)。

\(E.g.\) \(\sigma_7(2)=3\)

\(2^1\equiv 2(\mod 7)\)

\(2^2\equiv 4(\mod 7)\)

\(2^3\equiv 1(\mod 7)\)

对于一个数\(g\),\(g\)的阶一定是\(p-1\)的约数。

证明:

假设最小的\(k\)不是\(p-1\)的约数,找到\(x\)满足\(xk>p-1>(x-1)k\),由费马小定理可知

\[g^{xk}\equiv g^{p-1}\equiv 1 \equiv g^{xk-(p-1)} (\mod p)
\]

\(xk-(p-1)<k\),与假设矛盾。

原根

定义

\(FFT\)中,我们使用单位复根\(\omega_n^k=\cos k\frac{2\pi}{n}+i\sin k\frac{2\pi}{n}\),那有没有什么能够代替单位复根且解决精度问题呢?这就是原根。

设\(m\)是正整数,\(a\)是整数,若\(a\)模\(m\)的阶等于\(\varphi(m)\),则称\(a\)为模\(m\)的一个原根

若\(p\)为质数,设\(g\)为\(p\)的原根,那么\(g^i \mod p(1<j<p,1\le i\le p-1)\)的结果两两不同。且其等价于\(g^{p-1}\equiv 1(\mod p)\)当且仅当指数为\(p-1\)的时候成立。(这里\(p\)是素数)

简单证明一下:

显然\(g^0 \equiv 1(\mod p)\)

由原根的定义可知满足\(g^{i} \equiv 1(\mod p)\)的最小正整数为\(\varphi(p)=p-1\)

故由指数循环节可知,\(g^i \mod p(1<j<p,1\le i\le p-1)\)的结果两两不同。

性质

考虑在FFT当中我们需要单位复根的以下性质:

  1. \(\omega_n^t\)互不相同,保证点值的合法性;

  2. \(\omega_{2n}^{2k} = \omega_n^k\),用于分治;

  3. \(\omega_n^{k+\frac{n}{2}} = -\omega_n^k\),用于分治;

  4. 当\(k\neq 0\)时,\(1+\omega_n^k+(\omega_n^k)^2+\dots +(\omega_n^k)^{n-1}=0\),用于逆变换。

性质一

令\(\omega_n=g^q\),\(1,g^q,g^{2q},\dots,g^{(n-1)q}\)互不相同,满足性质一

性质二

由\(\omega_n = g^q\)可知,\(\omega_{2n}=g^{\frac{q}{2}}(p=\frac{q}{2} \times 2n + 1)\),故\(\omega_{2n}^{2k} = g^{2k \frac{q}{2}}=g^{kq}=g^q\),满足性质二

性质三

根据费马小定理得

\[\omega_n^n=g^{nq}=g^{p-1}\equiv 1(\mod p)
\]

又因为\((\omega_n^{\frac{n}{2}})^2=\omega_n^n\),所以\(\omega_n^{\frac{n}{2}}\equiv \pm 1 (\mod p)\),而根据性质一可得\(\omega_n^{\frac{n}{2}}\neq \omega_n^0\),即\(\omega_n^{\frac{n}{2}}\equiv -1(\mod p)\)。可推出\(\omega_n^{k+\frac{n}{2}}=\omega_n^k \times \omega_n^{\frac{n}{2}}=-\omega_n^k (\mod p)\),满足性质三

性质四

当\(k\neq 0\)时

\[S(\omega_n^k)=1+\omega_n^k+(\omega_n^k)^2+\dots +(\omega_n^k)^{n-1}
\]

\[\omega_n^k S(\omega_n^k)=\omega_n^k+(\omega_n^k)^2+(\omega_n^k)^3+\dots +(\omega_n^k)^n
\]

\[\omega_n^k S(\omega_n^k)-S(\omega_n^k)=(\omega_n^k)^n-1
\]

\[S(\omega_n^k)=\frac{(\omega_n^k)^n-1}{\omega_n^k-1}
\]

性质三中的推论可知,\((\omega_n^k)^n-1=(\omega_n^n)^k-1\equiv \omega_n^n-1\equiv 0 (\mod p)\),故\(S(\omega_n^k)=0\),性质四成立。

求原根

求一个质数的原根,可以用枚举法——枚举\(g\),检验\(g\)是否为\(p\)的原根。

根据前面的关于阶知识可知,检验时,只需枚举\(p-1\)的所有约数\(q\),检验\(g^q\equiv 1(\mod p)\)即可。

代码实现

将\(FFT\)里所有关于\(\omega_n\)的运算替换成\(g^q\)在模意义下的运算即可,注意\(\div n\)要改为\(\times n^{-1}\)

#include <bits/stdc++.h>
using namespace std; typedef long long ll;
inline ll ty() {
char ch = getchar(); ll x = 0, f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
} const int _ = 4e6 + 10;
const ll P = 998244353, G = 3, Gx = 332748118;
int N, M, r[_];
ll A[_], B[_]; ll ksm(ll a, ll b) {
ll ret = 1;
for ( ; b; b >>= 1) {
if (b & 1) ret = ret * a % P;
a = a * a % P;
}
return ret;
} void ntt(int lim, ll *a, int op) {
for (int i = 0; i < lim; ++i)
if (i < r[i]) swap(a[i], a[r[i]]);
for (int len = 2; len <= lim; len <<= 1) {
int mid = len >> 1;
ll Wn = ksm(op == 1 ? G : Gx, (P - 1) / len);
for (int i = 0; i < lim; i += len) {
ll w = 1;
for (int j = 0; j < mid; ++j, w = (w * Wn) % P) {
ll x = a[i + j], y = w * a[i + j + mid] % P;
a[i + j] = (x + y) % P;
a[i + j + mid] = (x - y + P) % P;
}
}
}
} int main() {
#ifndef ONLINE_JUDGE
freopen("ntt.in", "r", stdin);
freopen("ntt.out", "w", stdout);
#endif
N = ty(), M = ty();
for (int i = 0; i <= N; ++i) A[i] = (ty() + P) % P;
for (int i = 0; i <= M; ++i) B[i] = (ty() + P) % P;
int lim = 1, k = 0;
while (lim <= N + M) lim <<= 1, ++k;
for (int i = 0; i < lim ; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (k - 1));
ntt(lim, A, 1);
ntt(lim, B, 1);
for (int i = 0; i < lim; ++i) A[i] = (A[i] * B[i]) % P;
ntt(lim, A, -1);
ll invx = ksm(lim, P - 2);
for (int i = 0; i <= N + M; ++i)
printf("%lld ", (A[i] * invx) % P);
return 0;
}

参考资料

从傅里叶变换到数论变换 | Menci's Blog

快速数论变换(NTT)小结 - 自为风月马前卒 - 博客园

从傅里叶变换(FFT)到数论变换(NTT)的更多相关文章

  1. Algorithm: 多项式乘法 Polynomial Multiplication: 快速傅里叶变换 FFT / 快速数论变换 NTT

    Intro: 本篇博客将会从朴素乘法讲起,经过分治乘法,到达FFT和NTT 旨在能够让读者(也让自己)充分理解其思想 模板题入口:洛谷 P3803 [模板]多项式乘法(FFT) 朴素乘法 约定:两个多 ...

  2. 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/常用套路【入门】

    原文链接https://www.cnblogs.com/zhouzhendong/p/Fast-Fourier-Transform.html 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/ ...

  3. 【算法】快速数论变换(NTT)初探

    [简介] 快速傅里叶变换(FFT)运用了单位复根的性质减少了运算,但是每个复数系数的实部和虚部是一个余弦和正弦函数,因此系数都是浮点数,而浮点数的运算速度较慢且可能产生误差等精度问题,因此提出了以数论 ...

  4. 快速傅里叶变换(FFT)学习笔记(其二)(NTT)

    再探快速傅里叶变换(FFT)学习笔记(其二)(NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其二)(NTT) 写在前面 一些约定 前置知识 同余类和剩余系 欧拉定理 阶 原根 求原根 NTT ...

  5. 快速傅里叶变换FFT& 数论变换NTT

    相关知识 时间域上的函数f(t)经过傅里叶变换(Fourier Transform)变成频率域上的F(w),也就是用一些不同频率正弦曲线的加 权叠加得到时间域上的信号. \[ F(\omega)=\m ...

  6. 高速数论变换(NTT)

    今天的A题.裸的ntt,但我不会,于是白送了50分. 于是跑来学一下ntt. 题面非常easy.就懒得贴了,那不是我要说的重点. 重点是NTT,也称高速数论变换. 在非常多问题中,我们可能会遇到在模意 ...

  7. 快速数论变换(NTT)小结

    NTT 在FFT中,我们需要用到复数,复数虽然很神奇,但是它也有自己的局限性--需要用double类型计算,精度太低 那有没有什么东西能够代替复数且解决精度问题呢? 这个东西,叫原根 原根 阶 若\( ...

  8. JZYZOJ 2041 快速数论变换 NTT 多项式

    http://172.20.6.3/Problem_Show.asp?id=2041 https://blog.csdn.net/ggn_2015/article/details/68922404 代 ...

  9. [快速数论变换 NTT]

    先粘一个模板.这是求高精度乘法的 #include <bits/stdc++.h> #define maxn 1010 using namespace std; char s[maxn]; ...

随机推荐

  1. List<string>和string[]数组之间的相互转换,需要的朋友可以参考下

    1,从System.String[]转到List<System.String> System.String[] str={"str","string" ...

  2. How to: Initialize Business Objects with Default Property Values in Entity Framework 如何:在EF中用默认属性值初始化业务对象

    When designing business classes, a common task is to ensure that a newly created business object is ...

  3. FCC---CSS Flexbox: Apply the flex-direction Property to Create Rows in the Tweet Embed

    The header and footer in the tweet embed example have child items that could be arranged as rows usi ...

  4. synchronized凭什么锁得住?

    相关链接: <synchronized锁住的是谁?> 我们知道synchronized是重量级锁,我们知道synchronized锁住的是一个对象上的Monitor对象,我们也知道sync ...

  5. Windows 2012 R2 安装RD服务

    默认只能同时允许2个用户连接,如果希望更多用户同时连接服务器,需要开启并激活远程桌面服务.参考:https://jingyan.baidu.com/article/9f7e7ec0f5a8686f28 ...

  6. Linux发行版的系统目录名称命名规则以及用途

    linux各种发行版都遵循LSB(Linux Stadards Base)规则,使用一致的相关的基础目录名称,使用根目录系统结构(root filesystem),使用FHS(Files Hierar ...

  7. pytorch 中改变tensor维度的几种操作

    具体示例如下,注意观察维度的变化 #coding=utf-8 import torch """改变tensor的形状的四种不同变化形式""" ...

  8. JavaScript 7 获取可视窗口、网页元素、获取节点方式

    获取可视窗口对象 chrom *document.body firefox *document.documentElement 获取滚动条偏移量 ele.scrollTop ele.scrollLef ...

  9. Pipe——高性能IO(三)

    Pipelines可以替换掉那些丑陋的封装(kludge).变通(workaround)或妥协(compromise)——用一个在框架中设计优雅的专门的解决方案. 敢肯定,下面所覆盖的那些痛点,对于那 ...

  10. php中文乱码原因和维修方法

    一.首先是PHP网页的编码 1.如果欲使用gb2312编码,那么php要输出头:header(“Content-Type: text/html; charset=gb2312”),静态页面添加,所有文 ...