zoukankan      html  css  js  c++  java
  • 【基础操作】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$ 时都跑得不如暴力快。

  • 相关阅读:
    系统维护相关问题
    Python环境维护
    哈希表解决字符串问题
    论文笔记二:《A Tutoral on Spectral Clustering》
    论文笔记之哈希学习比较--《Supervised Hashing with Kernels》《Towards Optimal Binary Code Learning via Ordinal Embedding》《Top Rank Supervised Binary Coding for Visual Search》
    Java中String、StringBuffer、StringBuilder的比较与源 代码分析
    浙大pat1040 Longest Symmetric String(25 分)
    浙大pat1039 Course List for Student(25 分)
    浙大pat---1036 Boys vs Girls (25)
    百炼oj-4151:电影节
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/FFT.html
Copyright © 2011-2022 走看看