NTT

在FFT中,我们需要用到复数,复数虽然很神奇,但是它也有自己的局限性——需要用double类型计算,精度太低

那有没有什么东西能够代替复数且解决精度问题呢?

这个东西,叫原根

原根

若\(a,p\)互素,且\(p>1\),

对于\(a^n \equiv 1 \pmod{p}\)最小的\(n\),我们称之为\(a\)模\(p\)的阶,记做\(\delta_p(a)\)

例如:

\(\delta_7(2)=3\),

\(2^1 \equiv 2 \pmod{7}\)

\(2^2 \equiv 4 \pmod{7}\)

\(2^3 \equiv 1 \pmod{7}\)

原根

原根的定义

设\(p\)是正整数,\(a\)是整数,若\(\delta_p(a)\)等于\(\phi(p)\),则称\(a\)为模\(p\)的一个原根

\(\delta_7(3)=6=\phi(7)\),因此\(3\)是模\(7\)的一个原根

注意原根的个数是不唯一的

如果模数\(p\)有原根,那么它一定有\(\phi(\phi(p))\)个原根

原根存在的重要条件为\(m = 2,4,p^a,2p^{a}\),其中\(p\)为奇素数\(a \ge 1\)

例如

原根有一个非常重要的定理:

  • 若\(P\)为素数,假设一个数\(g\)是\(P\)的原根,那么\(g^i \mod P (1<g<P,0<i<P)\)的结果两两不同

不要问我为什么,因为我也不知道。。

考虑原根为什么能代替单位根进行运算,(这部分可以跳过)

原因很简单,因为它具有和单位根相同的性质

在FFT中,我们用到了单位根的四条性质,而原根也满足这四条性质

这样我们最终可以得到一个结论

\[\omega_n \equiv g^\frac{p-1}{n} \mod p
\]

然后把FFT中的\(\omega_n\)都替换掉就好了

\(p\)建议取\(998244353\),它的原根为\(3\)。

如何求任意一个质数的原根呢?

对于质数\(p\),质因子分解\(p−1\),若\(g^{\frac{p-1}{p_i}} \neq 1 \pmod p\)恒成立,\(g\)为\(p\)的原根

实现

NTT求卷积代码:

确实比FFT快了不少

#include<cstdio>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++)
#define swap(x,y) x ^= y, y ^= x, x ^= y
#define LL long long
const int MAXN = 3 * 1e6 + 10, P = 998244353, G = 3, Gi = 332748118;
char buf[1<<21], *p1 = buf, *p2 = buf;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N, M, limit = 1, L, r[MAXN];
LL a[MAXN], b[MAXN];
inline LL fastpow(LL a, LL k) {
LL base = 1;
while(k) {
if(k & 1) base = (base * a ) % P;
a = (a * a) % P;
k >>= 1;
}
return base % P;
}
inline void NTT(LL *A, int type) {
for(int i = 0; i < limit; i++)
if(i < r[i]) swap(A[i], A[r[i]]);
for(int mid = 1; mid < limit; mid <<= 1) {
LL Wn = fastpow( type == 1 ? G : Gi , (P - 1) / (mid << 1));
for(int j = 0; j < limit; j += (mid << 1)) {
LL w = 1;
for(int k = 0; k < mid; k++, w = (w * Wn) % P) {
int x = A[j + k], y = w * A[j + k + mid] % P;
A[j + k] = (x + y) % P,
A[j + k + mid] = (x - y + P) % P;
}
}
}
}
int main() {
N = read(); M = read();
for(int i = 0; i <= N; i++) a[i] = (read() + P) % P;
for(int i = 0; i <= M; i++) b[i] = (read() + P) % P;
while(limit <= N + M) limit <<= 1, L++;
for(int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
NTT(a, 1);NTT(b, 1);
for(int i = 0; i < limit; i++) a[i] = (a[i] * b[i]) % P;
NTT(a, -1);
LL inv = fastpow(limit, P - 2);
for(int i = 0; i <= N + M; i++)
printf("%d ", (a[i] * inv) % P);
return 0;
}

快速数论变换(NTT)小结的更多相关文章

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

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

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

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

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

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

  4. [快速数论变换 NTT]

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

  5. 模板 - 数学 - 多项式 - 快速数论变换/NTT

    Huffman分治的NTT,常数一般.使用的时候把多项式的系数们放进vector里面,然后调用solve就可以得到它们的乘积.注意这里默认最大长度是1e6,可能需要改变. #include<bi ...

  6. 快速数论变换NTT模板

    51nod 1348 乘积之和 #include <cmath> #include <iostream> #include <cstdio> #include &l ...

  7. 从傅里叶变换(FFT)到数论变换(NTT)

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

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

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

  9. 「算法笔记」快速数论变换(NTT)

    一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...

随机推荐

  1. Spring Cloud 微服务架构的五脏六腑,统统晒一晒!

    Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组 ...

  2. [Swift]LeetCode219. 存在重复元素 II | Contains Duplicate II

    Given an array of integers and an integer k, find out whether there are two distinct indices i and j ...

  3. [Swift]LeetCode594. 最长和谐子序列 | Longest Harmonious Subsequence

    We define a harmonious array is an array where the difference between its maximum value and its mini ...

  4. [Swift]LeetCode653. 两数之和 IV - 输入 BST | Two Sum IV - Input is a BST

    Given a Binary Search Tree and a target number, return true if there exist two elements in the BST s ...

  5. [Swift]LeetCode836. 矩形重叠 | Rectangle Overlap

    A rectangle is represented as a list [x1, y1, x2, y2], where (x1, y1) are the coordinates of its bot ...

  6. [Swift]LeetCode947. 移除最多的同行或同列石头 | Most Stones Removed with Same Row or Column

    On a 2D plane, we place stones at some integer coordinate points.  Each coordinate point may have at ...

  7. iOS学习——浅谈RunLoop

    RunLoop的字面意思是运行循环.跑圈,一个App启动后能一直执行,就是因为启动后进入了一个循环,在这个循环中不断监听各种状态.手势动作,并做出相应的响应.这个循环就是我们今天要探究的RunLoop ...

  8. Kubernetes系列之监控Metres-server实战篇

    本次系列使用的所需部署包版本都使用的目前最新的或最新稳定版,安装包地址请到公众号内回复[K8s实战]获取 介绍 从 Kubernetes 1.8 开始,Kubernetes 通过 Metrics AP ...

  9. python +selenium识别不来click事件,出现报错

    assert "login" in browser.title browser.implicitly_wait(10) elem = browser.find_element_by ...

  10. 汇编语言 实验14 访问CMOS RAM

    汇编语言 访问CMOS RAM CMOS RAM 是什么? 存放计算机开机必备的一些数据的硬件,由BIOS(Basic input/output system)调用其数据. 怎么用? 首先要理解端口的 ...