多项式相关——FFT(学习中……持续更新)

FFT

参考blog:
十分简明易懂的FFT(快速傅里叶变换)
快速傅里叶变换(FFT)详解

系数表示法

  一个一元\(n\)次多项式\(f(x)\)可以被表示为:\[f(x) = \sum_{i = 0}^{n}a_{i}x^{i}\]
  即用\(i\)次项的系数来表示\(f(x)\),展开就是\(f(x) = {a_{0}, a_{1}...a_{n}}\)
  

点值表示法

  把多项式看做一个函数,然后带入\(n\)个不同的\(x\),可以得到\(n\)个不同的\(y\),每对\((x, y)\)就组成一个点。
  其中,\(n\)个点可以唯一确定一个\(n\)次多项式。
  即用\(n\)个点来表示一个多项式
  

一些性质:

系数表达式相乘复杂度\(n^2\),点值表达式相乘复杂度\(O(n)\),听上去很神奇的样子。。。
设2个点值表达式分别为:
\(f(x) = \{(x_{0}, f(x_{0})), (x_{1}, f(x_{1}))... (x_{n}, f(x_{n}))\}\)
\(g(x) = \{(x_{0}, g(x_{0})), (x_{1}, g(x_{1}))... (x_{n}, h(x_{n})) \}\)
那么相乘得到:
\(h(x) = \{(x_{0}, f(x_{0}) * g(x_{0})), (x_{1}, f(x_{1}) * g(x_{1})) ... (x_{n}, f(x_{n}) g(x_{n})) \}\)

朴素系数转点值:DFT 复杂度\(O(n^2)\)
朴素点值转系数:IDFT 复杂度\(O(n^2)\)

复数

\(z = a + bi\),\(a\)为实部,\(b\)为虚部。
可以表示坐标系中的一个点\((a, b)\),同时一一对应向量\(\vec{ab}\),因此也符合向量的相加法则。
在极坐标上可以表示为\((r, \theta)\)。
一个性质:\((a_1, \theta_1) \cdot (a_2, \theta_2) = (a_1a_2, \theta_1 + \theta_2)\)
模长相乘,幅角相加

DFT(离散傅里叶变换)

  • 从这里开始的所有\(n\)默认可以表示为\(2^k\)
    原理:对于任意系数多项式转点值表示法,如果随意取\(n\)个\(x\)值代入计算,那么每次计算都是\(O(n)\)的,总复杂度\(O(n^2)\).
    如果取一些特殊的\(x\)值,使得\(f(x)\)可以快速计算,那么就可以在保证正确性的同时优化复杂度。

如果代入一些\(x\),使得每个\(x\)的若干次方等于\(1\),那么说不定我们就可以找到一些特殊性质。那么有哪些\(x\)符合这个条件呢?
显然\(\pm 1\)和\(\pm i\)都可以做到,但4个数明显不够用。

这个圆圈上面的点都可以做到.

以原点为圆心,画一个半径为1的单位圆,那么单位圆上的所有点都可以经过若干次方得到1.
对这个圆进行\(n\)等分。

以\(n = 8\)为例,从\((1, 0)\)开始,逆时针从\(0\)号开始标号,标到\(7\)号为止。记编号为\(k\)的点代表的复数为\(w_n^k\),那么由模长相乘,幅角相加可知\((w_n^1)^k = w_n^k\).
其中称\(w_n^1\)为\(n\)次单位根,并且每个\(w\)都可以被求出:
\[w_n^k = cos\frac{k}{n}2\pi + i \cdot sin\frac{k}{n}2\pi\]

但如果我们暴力代入图中的\(w_n^0,w_n^1...w_n^{n - 1}\),复杂度还是\(n^2\),因此我们考虑寻找一下单位根的性质

单位根的性质

\(w_n^k = w_{2n}^{2k}\)
证明:\[w_n^k = cos\frac{k}{n}2\pi + i \cdot sin\frac{k}{n}2\pi\]
\[w_{2n}^{2k} = cos\frac{2k}{2n}2\pi + i \cdot sin\frac{2k}{2n}2\pi\]

显然相等

\(w_n^{k + \frac{n}{2}} = - w_n^k\)
它们所代表的点关于原点对称,所代表的复数实部相反,所代表的向量等大反向
证明:\[w_n^{\frac{n}{2}} = cos\frac{\frac{n}{2}}{n}2\pi + i \cdot sin\frac{\frac{n}{2}}{n}2\pi\]
\[= cos\pi + i \cdot sin\pi = -1\]

补充2个等式:
\[e^{ix} = cosx + i \cdot sinx\]
\[e^{i\pi} + 1 = 0\]

\[w_n^0 = w_n^n\]
它们都等于\(1\),或者\(1 + 0i\)
\[(w_n^x)^y = w_n^{xy}\]

FFT(快速傅里叶变换)

目的:系数转点值。
设\[A(x) = \sum_{i = 0}^{n - 1}a_ix^i = a_0 + a_1x + a_2x^2+...+a_{n - 1}x^{n - 1}\]
按下标奇偶性把\(A(x)\)分成2半,右边再提一个x.
\[A(x) = (a_0 + a_2x^2 + ... + a_{n - 2}x^{n - 2}) + (a_1x + a_3x^3 + ... + a_{n - 1}x^{n - 1})\]
\[A(x) = (a_0 + a_2x^2 + ... + a_{n - 2}x^{n - 2}) + x(a_1 + a_3x^2 + ... + a_{n - 1}x^{n - 2})\]
设\[A_1(x) = a_0 + a_2x + a_4x^2 + ... + a_{n - 2}x^{\frac{n}{2} - 1}\]
\[A_2(x) = a_1 + a_3x + a_5x^2 + ... + a_{n - 1}x^{\frac{n}{2} - 1}\]
\[\Longrightarrow A(x) = A_1(x^2) + xA_2(x^2)\]

设\(k < \frac{n}{2}\),代入\(w_n^k = x \longrightarrow A(x)\)
\[\Longrightarrow A(w_n^k) = A_1((w_n^k)^2) + W_n^k A_2((w_n^k)^2)\]
\[ = A_1(w_n^{2k}) + w_n^k A_2(w_n^{2k})\]
\[ = A_1(w_{\frac{n}{2}}^k) + w_n^kA_2(w_{\frac{n}{2}}^k)\]
再代入\(k + \frac{n}{2}\)

再考虑另一半:

代入\(k + \frac{n}{2}\)
\[A(w_n^{k + \frac{n}{2}}) = A_1(w_n^{2k + n}) + w_n^{k + \frac{n}{2}}A_2(w_n^{2k + n})\]
可以发现:
\[w^{k + \frac{n}{2}}_n = w_n^k \cdot w_n^{\frac{n}{2}} = -w^k_n\]
\[w_n^{2k + n} = w_n^{2k} \cdot w_n^n = w_n^{2k}\]
因此可以得到:
\[A(w_n^{k + \frac{n}{2}}) = A_1(w_n^{2k}) - w_n^kA_2(w_n^{2k})\]
\[ = A_1(w_{\frac{n}{2}}^k) - w_n^kA_2(w_{\frac{n}{2}}^{k})\]

于是可以发现,这2个式子是长得很像的,因此我们可以在求出\(A(w_n^k)\)后\(O(1)\)的求出\(A(w_n^{k + \frac{n}{2}})\).

因为将式子一分为二后,每一部分仍然是一个子问题,因此可以用分治来做到\(nlogn\)求这个东西。

每次回溯时只扫前面一半序列,即可得到后面一半序列的答案,长度为1时只有一个常数项,可以直接返回。

大致就是把\(f(x)\)和\(g(x)\)分别转换为点值表达,然后\(O(n)\)的处理乘积,得到\(h(x)\)的点值表达

IFFT(快速傅里叶逆变换)

目的:点值转系数
设\((y_0, y_1, y_2..., y_{n - 1})\)为\((a_0, a_1, a_2, ..., a_{n - 1})\)的傅里叶变换(点值表达)。
设有另一个向量\((c_0, c_1, c_2, ..., c_{n - 1})\),满足\(c_k = \sum_{i = 0}^{n - 1}y_i(w_n^{-k})^i\).
即多项式\(B(x) = y_0 + y_1x + y_2x^2 + ... + y_{n - 1}x^{n - 1}\)在\(w_n^0, w_n^{-1},w_n^{-2}...w_{n - 1}^{-(n - 1)}\)处的点值表示。
于是对\(c_k = \sum_{i = 0}^{n - 1}y_i(w_n^{-k})^i\)进行化简
\[c_k = \sum_{i = 0}^{n - 1}y_i(w_n^{-k})^i\]
\[ = \sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^i)^j)(w_n^{-k})^i\]
\[ = \sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^j)^i)(w_n^{-k})^i\]
\[ = \sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^j)^i(w_n^{-k})^i)\]
\[ = \sum_{i = 0}^{n - 1}\sum_{j = 0}^{n - 1}a_j(w_n^j)^i(w_n^{-k})^i\]
\[ = \sum_{i = 0}^{n - 1} \sum_{j = 0}^{n - 1}a_j(w_n^{j - k})^i\]
\[ = \sum_{j = 0}^{n - 1}a_j (\sum_{i = 0}^{n - 1}(w_n^{j - k})^i)\]

设\(S(n) = \sum_{i = 0}^{n - 1}x_i\),将\(w_n^k\)代入得:\[S(w_n^k) = 1 + (w_n^k) + (w_n^k)^2 + ... + (w_n^k)^{n - 1}\]
当\(k != 0\)得,等式两边同乘\(w_n^k\)得:\[w_n^kS(w_n^k) = w_n^k + (w_n^k)^2 + ... + (w_n^k)^n\]
两式相减得:
\[w_n^kS(w_n^k) - S(w_n^k) = (w_n^k)^n - 1\]
\[S(w_n^k) = \frac{(w_n^k)^n - 1}{w_n^k - 1}\]
\[S(w_n^k) = \frac{(w_n^k)^n - 1}{w_n^k - 1}\]
\[S(w_n^k) = \frac{1 - 1}{w_n^k - 1} = \frac{0}{w_n^k - 1}\]
\(\longrightarrow\)分子为0,分母不为0

  • 当\(k != 0\)时,\(S(w_n^k) = 0\);\(\quad\)当\(k = 0\)时,\(S(w_n^0)\)
    继续考虑刚才的式子:\(c_k = \sum_{i = 0}^{n - 1}y_i(w_n^{-k})^i\)
  • 当\(j != k\)时,值为\(0\);\(\quad\)当\(j = k\)时,值为\(n\)
    因此:\(c_k = na_k \Longrightarrow a_k = \frac{c_k}{n}\)
    于是我们得到了一个\(O(1)\)把一个点值变成一个系数的方法。

递归实现

不断将当前序列一分为二,递归求解。
但效率过低……

迭代实现


观察到原序列和要求的序列之间有神奇的联系,,,
要求的序列的第i项就是原序列下标二进制的翻转。
因此我们可以\(O(n)\)预处理出要求的序列是怎么排的,然后再不断向上合并。

原文地址:https://www.cnblogs.com/ww3113306/p/10234916.html

时间: 2024-10-09 05:09:41

多项式相关——FFT(学习中……持续更新)的相关文章

博弈论 (学习中……持续更新)

博弈论 以下主要内容来自于对集训队论文<组合游戏略述--浅谈SG游戏的若干拓展及变形>的整理与从其他地方收集补充的一些经典模型 博弈论还在学习过程中,可能还会补充一些东西 组合游戏基础定义 游戏的定义: 游戏有2名参与者,两人轮流操作 游戏过程中的任意时刻有确定的状态 参与者操作时将游戏从当前状态转移到另一状态,且规则规定了在任意状态时,可以到达的状态集合 在有限步数之内结束(没有平局) 参与者拥有完全的信息 游戏的表示: 定义:对于一个游戏,如果当前状态\(P\),玩家\(L\)可以转移到的

linux学习资料持续更新中

一.LINUX基础教程 1.老男孩系列免费视频: 1) linux高薪入门实战视频教程(第二部)老男孩linux教程 http://edu.51cto.com/course/course_id-1035-page-1.html 2) 跟着老男孩从0开始一步步实战深入学习linux运维(三) http://edu.51cto.com/lesson/id-11909.html linux学习资料持续更新中,布布扣,bubuko.com

nodejs学习(持续更新中)

nodejs和express的安装什么的,网上基本都有现成的了,这里有点说下, 在较早点的版本(如3.5.0) npm install -g [email protected] 后,可以直接使用 express helloWorld创建工程, 但最新express4.0版本中将命令工具分家出来了(项目地址:https://github.com/expressjs/generator),所以我们还需要安装一个命令工具,命令如下:npm install -g express-generator ##

Arduino语言学习记录(持续更新)

几天前某宝买了一套,这几天没工夫.今天开始学学这个“玩具”. 1.Arduino的变量数据类型: 数据类型  数据类型 RAM 范围 void keyword N/A N/A boolean 1 byte 0 到 1(True 或 False) byte 1 byte 0 到 255 char 1 byte -128 到 127 unsigned char 1 byte 0 到 255 int 2 byte -32768 到 32767 unsigned int 2 byte 0 到 65535

多项式的各种运算总结(持续更新)

多项式的各种运算总结(持续更新) 多项式 多项式是个啥呢? 我们通常说的都是一元的多项式,所以一个多项式可以写成形如: \(a_ 0+a_ 1x+a_ 2x^2+a_ 3x^3......\)的式子 注意到,真正有用的是数列\(\{a_i\}\) 但是一旦我们要涉及到什么运算,就会发现对于\(\{a_i\}\)的某些运算不是特别方便. 所以我们定义生成函数\(A(x)=\sum_{i=0}^{\infty}a _ix^i\).\(A(x)\)就称为数列\(\{a _i\}\)的生成函数. 通过生

多项式乘法(FFT)学习笔记

------------------------------------------本文只探讨多项式乘法(FFT)在信息学中的应用如有错误或不明欢迎指出或提问,在此不胜感激 多项式 1.系数表示法     一般应用最广泛的表示方式     用A(x)表示一个x-1次多项式,a[i]为$ x^i$的系数,则A(x)=$ \sum_0^{n-1}$ a[i] * $ x^i$ 仅利用这种方式求多项式乘法复杂度为O($ n^2$),不够优秀2.点值表示法     将n个互不相同的值$ x_0$...$

[Hadoop] Hadoop学习历程 [持续更新中…]

1. Hadoop FS Shell Hadoop之所以可以实现分布式计算,主要的原因之一是因为其背后的分布式文件系统(HDFS).所以,对于Hadoop的文件操作需要有一套全新的shell指令来完成,而这就是Hadoop FS Shell.它主要是用于对Hadoop平台进行文件系统的管理. 有关HDFS的介绍博客请移步:Hadoop学习笔记之Hadoop基础. 有关Hadoop FS Shell的学习文档:Hadoop FS Shell学习文档. 2. Hadoop Streaming 我们知

图像处理 基于Visual C++编程 学习笔记 持续更新中。。。

2015-4-26 新建一个工程,安装MSDN文档 File -new - win32application- a simple win32 app Dos操作系统是 16位操作系统 2^16=65535 ,内存为16k win32操作系统(window95以后的系统) 32位 2^32 内存约为4G 进入后可以试着编译运行这样一段话 tip:选中MessageBox 按F1可以看到MSDN的相关文档, 选中MB_OK 按F12可以看到它的宏定义 int MessageBox( HWND hWn

Flask,web框架学习_持续更新

<Flask web 开发:基于python> Always believe that something wonderful is about to happen 第1章 安装 1.1virtualenv模块安装 #在shell窗口 easy_install virtualenv #即可 或者 pip isntall virtualenv #查看版本 virtualenv --version 1.2创建虚拟环境venv 及激活 virtualenv venv#venv为名字,可随意改,但常用