首页 > 其他分享 >快速傅里叶变换计算多项式乘法

快速傅里叶变换计算多项式乘法

时间:2023-09-13 12:22:28浏览次数:47  
标签:dots frac limits 多项式 sum 点值 傅里叶 乘法

前言

OI 中,多项式有着十分广泛的应用。其基础是多项式的基本运算,几乎所有多项式运算都是由多项式加法和乘法拼接成的。我们有显然的 \(O(n)\) 的办法计算多项式加法,而朴素的多项式乘法是很多情况下难以接受的 \(O(n^2)\) 的复杂度。快速傅里叶变换(FFT)可以高效(\(O(n\log n)\))计算多项式乘法,因此成为了多项式科技中几乎最重要的基础。

这篇总结是对 FFT 核心原理的一个不严谨的描述。

FFT

Part 1. 多项式的表示

对于一个多项式,\(f(x)=a_0+a_1x+a_2x^2+\dots+a_nx^n\),我们称它的次数为 \(n\),项数为 \(n+1\),任意大于等于 \(n\) 的正整数都叫 \(f(x)\) 的次数界。

由多项式的定义,我们可以用长度为 \(n+1\) 的向量 \(\begin{bmatrix}a_0,a_1,a_2,\dots,a_n \end{bmatrix}\) 来表示这个多项式。我们称这种表示方式为多项式的系数表示。

考虑把多项式 \(f(x)\) 视作平面直角坐标系上的函数,如果我们知道 \(n+1\) 个横坐标不同的点 \((x_0,y_0),(x_1,y_1)\dots,(x_n,y_n)\) 在其图象上,可以列出线性方程组:

\(\begin{cases}a_0+x_0a_1+x_0^2a_2+\dots+x_0^na_n=y_0\\a_0+x_1a_1+x_1^2a_2+\dots+x_1^na_n=y_1\\ \qquad \dots \qquad \dots \\ a_0+x_na_1+x_n^2a_2+\dots+x_n^na_n=y_n \end{cases}\)

线性代数知识(链接) 可知,该方程组有唯一解 \((a_0,a_1,a_2,\dots,a_n )\)。

这表明,我们可以通过 \(n+1\) 个横坐标互不相同的点表示一个 \(n\) 次多项式。事实上,给定这 \(n+1\) 个点的横纵坐标,我们可以通过高斯消元(\(O(n^3)\))或拉格朗日插值(\(O(n^2)\))计算出多项式的系数。

点值表示的多项式有个好处是:如果我们知道两个 \(n\) 次多项式 \(A,B\) 的点值表示,取了 \(2n+1\) 个点,并且取的 \(2n+1\) 个点的横坐标对应相同,我们可以 \(O(n)\) 的计算出多项式 \(A\times B\) 在这 \(2n+1\) 个点的值,进而可以算出 \(A\times B\) 的系数表示。

这样我们找到了一种新的计算多项式乘法的方式,先想办法求出 \(A,B\) 各自在某 \(2n+1\) 个点的取值,再 \(O(n)\) 计算每个位置点值乘积,再想办法把这些点值变成系数表示。也就是下面这张经典的关系图:

Part 2. 离散傅里叶变换(DFT)

假设我们要计算 \(m_1\) 次多项式 \(A\) 和 \(m_2\) 次多项式 \(B\) 的乘积,设 \(n\) 是大于 \(m_1+m_2\) 的最小的 2 的整次幂。

我们希望在 \(O(n\log n)\) 的时间内计算 \(n\) 个点在 \(A\) 的取值。随意选点至少要 \(O(n^2)\) 的时间计算点值。所以我们要在选的点上做文章。

我们引入复数单位根。设 \(n\) 是 2 的整次幂,则 \(w_n=\cos(\frac{2\pi}{n})+i\sin(\frac{2\pi}{n})\)。其几何意义是复平面上直线 \((0,0)\) - \((1,0)\),逆时针旋转 \(\frac{2\pi}{n}\) 弧度,与单位圆的交点。

记 \(w_n^u=\cos(\frac{2\pi u}{n})+i\sin(\frac{2\pi u}{n})\)

不难验证复数单位根有以下性质:

  • \(w_n^0=1\)

  • \(w_n^a \times w_n^b = w_n^{a+b}\) (可通过三角恒等式验证)

  • \(w_n^a=w_n^{a\bmod n}\)

  • \(w_{dn}^{dk}=w_n^k\),也称作消去引理

  • \(w_{n}^{d}=-w_{n}^{d+\frac{n}{2}}\)

我们要去求出 \(A(w_n^0),A(w_n^1),\dots,A(w_n^{n-1})\)。

设多项式 \(A_0=a_0+a_2x+a_4x^2+\dots+a_{n-2}x^{\frac{n-2}{2}}\),\(A_1=a_1+a_3x+a_5x^2+\dots+a_{n-1}x^{\frac{n-2}{2}}\)。那么

\(\begin{aligned} &A=a_0+a_1x+a_2x^2+\dots+a_{n-1}x^{n-1} \\ &=A_0(x^2)+xA_1(x^2)\end{aligned}\)

根据消去引理,\((w_n^k)^2=w_{\frac{n}{2}}^k\)。又因为 \(w_{\frac{n}{2}}^k=w_{\frac{n}{2}}^{k\bmod \frac{n}{2}}\) 所以实际用到的 \(A_0\) 和 \(A_1\) 的点值各自只有 \(\dfrac{n}{2}\) 个。递归计算 \(A_0\) 和 \(A_1\) 在这些点处的取值,可以做到 \(O(n)\) 合并出 \(A\) 的点值。

时间复杂度 \(T(n)=2T(\dfrac{n}{2})+O(n)=O(n\log n)\)

至此,我们完成了在 \(n\) 次复单位单位根处多项式 \(A\) 的点值的计算。

Part 3. 逆离散傅里叶变换(IDFT)

我们求出了 \(A\) 和 \(B\) 在 \(n\) 个复单位根处的取值,将对应点点值相乘,得到答案多项式 \(C\) 在这些点的取值, \(\{(w_n^0,C(w_n^0)),(w_n^1,C(w_n^{1})),\dots,(w_n^{n-1},C(w_n^{n-1}))\}\)。

设多项式 \(F(x)=C(w_n^0)+C(w_n^1)x+C(w_n^2)x^2+\dots+C(w_n^{n-1})x^{n-1}\)。设 \(C\) 的 \(i\) 次项的系数为 \(c_i\),将 \(w_n^{-k}\) 带入 \(F(x)\) 得:

\(\begin{aligned} & F(w_n^{-k})\\ &=\sum\limits_{i=0}^{n-1}C(w_n^i)w_n^{-ik} \\& =\sum\limits_{i=0}^{n-1} w_n^{-ik} \sum\limits_{j=0}^{n-1} c_j w_n^{ij} \\ & =\sum\limits_{j=0}^{n-1} c_j \sum\limits_{i=0}^{n-1} w_n^{i(j-k)} \end{aligned}\)

设 \(T=\sum\limits_{i=0}^{n-1} w_n^{i(j-k)}\)。

我们有 \(w_n^{j-k}T=\sum\limits_{i=1}^{n} w_n^{i(j-k)}\)

进而,\((w_n^{j-k}-1)T=w_n^{n(j-k)}-w_n^{0}=0\)

如果 \(j=k\) 那么显然 \(T=n\)

否则 \(w_n^{j-k}-1\neq 0\),\(T=0\)。

所以 \(F(w_n^{-k})=nc_k\)。

类似 DFT,把 \(w_n^0,w_n^{-1},\dots,w_n^{1-n}\) 带入 \(F\) 求值,我们只需要再 \(O(n)\) 计算 \(c_k=\dfrac{F(w_n^{-k})}{n}\) 就可以得到目标多项式。

时间复杂度 \(T(n)=O(n\log n)\)。

Part 4. 总结

综上,我们通过 DFT 和 IDFT 完成了 \(O(n\log n)\) 计算多项式乘法。

标签:dots,frac,limits,多项式,sum,点值,傅里叶,乘法
From: https://www.cnblogs.com/FreshOrange/p/17699267.html

相关文章

  • 多项式模板
    总算把之前摸鱼多项式欠下的东西还清了些。。。常数应该不算特别大点击查看代码namespacePolys{#definePolystd::vector<int>#definelllonglongconstintG=3,MOD=998244353;llpower(lla,llb=MOD-2){llret=1;......
  • 多项式全家桶(未全)
    一些约定:下面\(f^i(x)\)表示\(i\)阶导数。\(f(x)^i\)表示幂次。若不说明绝大部分除一个多项式时都是代表乘上它的逆。多项式加减,求导积分过于简单不讲。\((x^a)'=ax^{a-1},\displaystyle\intx^a{\rmd}x=\dfrac{x^{a+1}}{a+1}+C\)。实际操作时\(C=0\),正确性是好证的。(如......
  • 多项式ln
    给出\(n-1\)次多项式\(F(x)\),求一个\(\bmod{\:x^n}\)下的多项式\(B(x)\),满足\(g(x)\equiv\lnf(x)\)(\(f_0=1\))。\[g'(x)=\ln'(f(x))\timesf'(x)=\frac{f'(x)}{f(x)}(\bmodx^n)\]求导,求逆,求积即可。LLTmp[N];voidPolyInv(LL*a,LL*I,LL......
  • 编写函数计算多项式的值
    编写函数计算多项式的值题目:编写函数fun(),实现计算并返回多项式s=1+1/(1+2)+1/(1+2+3)+...+1/(1+2+3+...+n)的值。#include<stdio.h>#include<math.h>floatfun(intm){intq,p=0;floatw;for(q=1;q<=m;q++){p+=q;}w=1.0/p;returnw;}......
  • Matlab短时傅里叶变换和小波变换的时频分析
    ✅作者简介:热爱科研的算法开发者,Python、Matlab项目可交流、沟通、学习。......
  • AA@多项式@余式定理@根和一次因式的关系
    文章目录多项式函数余数定理(余式定理)根(零点)重根和单根根与一次因式的关系......
  • Verilog实现定点乘法器
    实验目的理解定点乘法的不同实现算法的原理,掌握基本实现算法。熟悉并运用Verilog语言进行电路设计。为后续设计CPU的实验打下基础。实验内容定点乘法器有多种实现,实验要求实现迭代乘法器,其结构如图所示。乘数每次右移一位,根据最低位,判断是加被乘数移位后的值还是加0,......
  • python函数的应用(一)九九乘法表
    函数实现99乘法表的打印#1.使用函数重构乘法口诀表并调用defmultiplication(n):foriinrange(1,n+1):forjinrange(1,i+1):print(j,"*",i,"=",j*i,end="\t")print()#调用函数a=int(input("请输入您想打印的乘法口诀表部分"))mult......
  • 99乘法表(递推版)
    #include<iostream>usingnamespacestd;voidcfb(inta,intb){if(a<=9){if(b<=a){cout<<b<<"x"<<a<<"="<<a*b<<"";cfb(a,b+1);}else......
  • 递归乘法表
    #include<bits/stdc++.h>usingnamespacestd;voidno(inta,intb){ if(b<=9){ if(a<=b){ cout<<a<<"*"<<b<<"="<<a*b<<""; no(a+1,b); }else{ cout<<endl; no(1,b+1); } ......