• 【基础操作】FFT / DWT / NTT / FWT 详解


    1.

    2. 点值表示法

    假设两个多项式相乘后得到的多项式 的次数(最高次项的幂数)为 $n$。(这个很好求,两个多项式的最高次项的幂数相加就得到了)

    对于每个点,要用 $O(n)$ 的时间 把 $x$ 分别代入两个多项式,得到两个结果 $z_1,z_2$,两者相乘得到 $z$,才能知道相乘后的多项式在代入一个 $x$ 时会得到 $z$,也就是固定了一个点 $(x,z)$。

    至少需要 $n$ 个点(也就是枚举 $n$ 个 $x$)才能确定一个 $n$ 次多项式(拉格朗日插值),总时间复杂度 $O(n^2)$,跟暴力差不多。

    $FFT$(Fast Fourier transform,快速傅里叶变换)

    $DFT$

    即把多项式从系数表示法转成点值表示法

    先考虑暴力做法。我们要选 $n$ 个 $x$ 代入多项式 $A(x)$。因为代入单位根时,$ ext{IDFT}$ 会很方便(只需要在最后把所有系数除以 $n$),所以我们令它们为 $n$ 次单位根的 $0$ 到 $n-1$ 次方(关于单位根和单位圆的知识移步这里)。注意由于单位根是复数,所以点值表示法的 $n$ 个坐标的横纵坐标也都是复数。

    $IDFT$

    即把多项式从点值表示法转成系数表示法

    参考此文“为什么要使用单位根作为 $x$ 代入”及“一个结论”部分。

    $NTT$(Number-Theoretic Transform,快速数论变换)

    在 $FFT$ 中,我们需要用到复数,复数虽然很神奇,但是它也有自己的局限性——

        - 需要用 $double$ 类型计算,精度太低;

        - 复数是虚数,它里面的 $i=sqrt{-1}$,并不能取模。

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

    这个东西,叫原根

    对于高中的 $OIer$ 而言,暂时不用证明这种没法证明的数学知识,记个结论就行了,其实现也只需要在普通的 $FFT$ 上改一点点。

    若 $a,p$ 互素,且 $p>1$,

    对于 $a^n≡1 pmod p$ 最小的 $n$,我们称之为 $a$ 模 $p$ 的阶,记做 $delta_p(a)$。也就是 $n=delta_p(a)$。

    例如:

    $δ7(2)=3$

    $2^1≡2space pmod7$

    $2^2≡4space pmod7$

    $2^3≡1space pmod7$

    原根

    定义:设 $p$ 是正整数,$a$ 是整数,若 $delta_p(a)$ 等于 $varphi(p)$,则称 $a$ 为模 $p$ 的一个原根。

    $delta_7(3)=6=varphi(7)$,因此 $3$ 是模 $7$ 的一个原根。

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

    这位同学讲得真好!

    辅助结论(暂时对代码无帮助)

    1. 如果模数 $p$ 有原根,那么它一定有 $varphi(varphi(p))$ 个原根。也就是说有些整数 $a$ 可能没有原根。

        其实原根存在的充要条件为 $p ∈ {1,2,4,p^n}$,其中 $p$ 为奇素数且 $n$ 是任意正整数。

    2. 若 $P$ 为素数,假设一个数 $g$ 是 $P$ 的原根,那么 $g^imod Pspace (1lt glt P,space 0lt ilt P-1)$ 的结果两两不同。

    用处

    原根能代替单位根进行运算,是因为它具有和单位根相同的性质。

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

    最终有个结论:$$omega_n equiv g^frac{p-1}{n} mod p$$

    然后把 $FFT$ 中的 $omega_n$ 全都换成上面这个即可。

    由于多出来的快速幂是套在最外层的 $log$ 级别循环里的,因此 $NTT$ 的时间复杂度跟 $FFT$ 一样,是 $O(n imes log(n))$ 的(但是带大常数,因为分治内快速幂部分的复杂度实际上略高于 $O(log(n))$)。

    怎么用代码求原根

    $OI$ 的常见模数是 $P=998244353$,其原根 $n$ 为 $3$,原根的逆元为 $998244354÷3=332748118$(做 $IDFT$ 时要把之前 $DFT$ 乘的项除掉,而除法得改成乘逆元)。这个模数可以直接背。

    如果一道题给了其它的模数,你可以在本机根据定义暴力求原根

    首先考虑求 $varphi(p)$。

    判断一下模数是不是素数($O(sqrt n)$ 就能判断,很快的),如果是的话,$varphi(n)$ 就等于 $n-1$(忘了的建议回去复习下欧拉函数);

    否则求 $varphi(n)$ 的值(其实时间复杂度也是 $O(sqrt n)$),本机跑完后取出结果就行啦。

    从小到大枚举正整数为原根 $a$,如果满足 $a^nequiv 1space ext{mod p}$ 的最小的 $n$ 是 $varphi(p)$,则 $a$ 就是 $p$ 的原根。

    由于你只需要在本机跑出一个原根,提出来即可,所以不用太管时间复杂度……

    好像结束了吧(这部分锅了,请无视)

    不,显然还没结束。不知道你有没有注意到这样一个问题:$a^n≡1 pmod p$ 这个式子,如果 $a$ 取 $1$,那它在定义域内肯定是满足要求的,也就是说 $1$ 好像是可以直接取的通用原根?

    那我们还暴力找原根干啥?

    这说明你忘了原根的意义。

    原根是用来代替 $FFT$ 中的 $omega$ 的,而我们用 $omega$ 是为了取 $n$ 个有特殊性质的不同点

    对,点值表示法一开始就说过,必须代入 $n$ 个不同的点,也就是 $n$ 个不同的取值 $x$。

    显然,如果取 $a=1$,那 $a^n$ 的所有取值都相同,就不满足点值表示法的本质要求了。

    所以原根不能取 $1$。

    对于 $a$ 的其它取值,之前还说过一个辅助结论,进一步印证了 取不等于 $1$ 且在范围内的原根都是满足要求的:

    若 $P$ 为素数,假设一个数 $g$ 是 $P$ 的原根,那么 $g_imod Pspace (1lt glt P,space 0lt ilt P)$ 的结果两两不同。

    FWT(Fast Walsh-Hadamard Transform,快速沃尔什变换)

    结语

    以前看到 $FFT$ 什么的数论神仙知识,总感觉很恶心,不是我这种蒟蒻可学的。

    但现在真的学,加起来其实只学了 $2$ 天,就差不多搞明白了(吗)。

    $OI$ 界的数学知识,并不要求你都严格证明,有些太叼的数学知识你可以先记个结论。

    另外 $FFT$ 什么的东西都是数学板子,全文背诵都可以……

    忠告:$FFT$ 系列的板子的常数都比较大,大约在 $nle 200$ 时都跑得不如暴力快。

  • 相关阅读:
    Express本地测试HTTPS
    在 WebStorm 中,配置能够识别 Vue CLI 3 创建的项目的别名 alias
    在线版本的ps
    功能强大的任务日历组件
    tree-shaking实战
    深入diff 算法
    【题解】[SHOI2001] Panda 的烦恼
    【题解】[JLOI2011]不重复数字
    「Codeforces Global Round #10」赛后个人总结
    【题解】[SCOI2004] 文本的输入
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/FFT.html
Copyright © 2020-2023  润新知