首页 > 其他分享 >FFT 学习笔记

FFT 学习笔记

时间:2023-06-14 18:33:59浏览次数:50  
标签:dots limits sum FFT 笔记 学习 我们 2k

首先就是中考这几天我们学校做考场,然后初二放假在家写作业。

然后我就摸鱼来推之前不会的 FFT 的式子,推一推发现诶麻麻我懂了!麻麻我悟了麻麻!

于是在放假第二天我写下了这样一篇学习笔记 qwq

多项式的系数表示和点值表示

我们都知道,一个 \(n\) 项多项式,如果我们写成一个函数,就可以写成这样:

\(f(x) = a_0 + a_1x + a_2x^2 + a_3x^3 + \dots + a_{n - 1}x^{n - 1}\)

如果我们有两个 \(n\) 项的多项式 \(f\) 与 \(g\),然后我们想要算它们俩的乘积应该怎么办呢?似乎我们只能 \(O(n^2)\)。

我们都知道,两点确定一个一次函数,三点确定一个二次函数,推广开来,\(n\) 个点可以确定一个 \(n\) 项的多项式。如果我们取出了 \(n\) 个横坐标 \(\{x_1, x_2, \dots, x_n\}\),然后我们算出来了 \(f\) 与 \(g\) 它们对应的横坐标的点集,\(\{(x_1, f(x_1)), (x_2, f(x_2)), \dots, (x_n, f(x_n))\}\) 和 \(\{(x_1, g(x_1)), (x_2, g(x_2)), \dots, (x_n, g(x_n))\}\)

横坐标不变,纵坐标相乘,得到新的 \(n\) 个点 \(\{(x_1, f(x_1)g(x_1)), (x_2, f(x_2)g(x_2)), \dots, (x_n, f(x_n)g(x_n))\}\) 这 \(n\) 个点一定在 \(f\) 与 \(g\) 的乘积的函数上。

这是相当好理解的。由于 \(n\) 点确定一个 \(n\) 项式,事实上我们已经知道了 \(f\) 乘 \(g\) 对不对?时间复杂度就是刚刚的相乘,也就是 \(O(n)\)。

快去开香槟!但事实上我们求点的复杂度为 \(O(n^2)\)。由于正常人都不会用 \(n\) 个点表示一个 \(n\) 项式,所以我们还要把这 \(n\) 个点转化成系数表达法才行,然后就要用高斯消元,时间复杂度 \(O(n^3)\)。

那我还不如暴力对吧,但是,事实上这给了我们启发,有没有一种方法能够取一些非常厉害的 \(x\),能让我们加速求出 \(n\) 个对应点的纵坐标(这叫插值)加速求出系数(这叫还原) 呢?

这就有了傅里叶变换了~


一些前置知识

虚数和复数

1 虚数

我们定义 \(i = \sqrt{-1}\),\(i\) 就是虚数单位。你可能会像曾经的我一样纠结于 \(i\) 是啥啊它是啥数啊它究竟是什么啊 QAQ 之类的问题。但是事实上 \(i\) 啥都不是,反正就是个符号(

2 复数

这可不是我们说的负数,而是复数,就是一个实数和一个虚数构成,一般用 \(z\) 表示,记为 \(z = a + bi\),其中 \(a\) 为实数,叫 \(z\) 的实部,\(bi\) 为虚数,叫 \(z\) 的虚部。

值得一提的是,\(z\) 可以在平面直角坐标系上用向量清晰地刻画出来。如图,从源点到点 \((2, 3)\) 构成一个向量,这条向量就代表复数 \(z = 2+3i\)。这里的纵轴叫虚轴(纵坐标代表虚部的 \(b\)),横轴叫实轴(横坐标代表实部的 \(a\))

pCnRauD.png


3 单位圆和单位根

在上图突兀的闯入了一个以原点为圆心,\(1\) 为半径的圆,这个圆很厉害,它的名字叫单位圆。单位圆有什么用呢?如果我们将单位圆 \(n\) 等分,画出 \(n\) 条半径,它们所对应的复数是 \(x^n = 1\) 的一个复数根。
pCnfVyT.png
比如这张图 \(n = 8\)(其实我标点的时候想标成 \(w_n^k\) 但是这个只能给我标成这个样子,大家凑合看吧 qwq)

从 \((1, 0)\) 开始将单位圆平分成 \(8\) 等分,有 \(8\) 条半径,从 \(0\) 开始逆时针编号,一直到 \(n - 1\)。每一个半径都对应着一个复数,我们记第 \(k\) 条半径的这个复数为 \(w_n^k\),很显然,\(w_n^0 = 1\)。其中 \((w_n^k)^n = 1\),具体而言我不会证(

然后呢?由于我们学过三角函数,我们容易得到 \(w_n^k = \cos(2\pi k/n) + i \sin(2\pi k / n)\)。同时还有 \(w_n^k = (w_n^1) ^k\),也就是说这个 \(k\) 可以看成 \(w_n^1\) 的指数,而不仅仅是一个上标(这条我也不会证,我太菜了 QAQ)


4 单位根的性质

  1. 折半定理
    \(w_n^k = w_{2n}^{2k}\)。
    相信大家看看上面的图就懂了。如果喜欢理性荔枝地证明的话——
    \(w_n^k = \cos(2\pi k / n) + i \sin(2\pi k / n)\\ = \cos(2\pi (2k / 2n)) + i \sin(2\pi(2k / 2n)) \\ = w_{2n}^{2k}\)

  2. 消去定理
    \(w_n ^k = -w_n ^{k - n / 2}\)(\(n\) 是偶数,\(k \le n / 2\))。
    一看图相信大家都明白啦,因为它俩关于原点对称,等大反向。


相信这些对大家都不难!了解了这些,FFT 就很容易啦~


FFT

插值

FFT 是用分治做的,所以下面我们默认 \(n\) 为 \(2\) 的整数次幂。

FFT 的精华在于倒式子,这个式子虽然很长,但是很好理解,相信大家都能看懂!ヾ(◍°∇°◍)ノ゙

\(f(x) = a_0 + a_1x + a_2x^2 + \dots a_{n - 1}x^{n - 1} \\ = (a_0 + a_2x_2 + a_4x_4 + \dots a_{n - 1}x^{n - 1}) + \\ (a_1x+ a_3x^3 + a_5x^5 + \dots a_{n - 2}x^{n - 2})\)

这一步是将下标按照奇偶性分类。我们定睛一看,发现这两边很相似,很漂亮,为了化简我们新定义两个函数

\(A_1(x) = a_0 + a_2x + a_4x^2 + \dots + a_{n - 1}x^{(n - 1) / 2}\)
\(A_2(x) = a_1 + a_3x + a_5x^2 + \dots + a_{n - 1}x^{(n - 1) / 2 - 1}\)
然后 \(f(x) = A_1(x^2) + xA_2(x^2)\)

FFT 最巧妙的一步在于,它代入的是 \(n\) 的单位根们,\(w_n^1, w_n^2, \dots w_n^{n - 1}\)。

所以就有 \(f(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})\)

第三步是因为 \(w_n ^k\) 的上标可以看成 \(w_n ^1\) 的指数

下面简单的分讨一下。

对于 \(k \le n / 2\),\(f(w_n^k) = A_1(w_n^{2k}) + w_{n}^k A_2(w_n^{2k}) \\ = A_1(w_{n / 2}^k) + w_n^kA_2(w_{n / 2}^k)\)。这是因为折半定理

对于 \(k > n / 2\),\(w_n^k = -w_{n} ^{k - n / 2}\),两边同时平方一下就有了 \(w_n^{2k} = w_n^{2k-n}\),再用折半定理,就能得到 \(w_{n / 2} ^k = w_{n / 2} ^{k - n / 2}\)。

所以呢 \(f(w_n^k) = A_1(w_n^{2k}) + w_{n}^k A_2(w_n^{2k}) \\ = A_1(w_{n / 2}^k) + w_n^kA_2(w_{n / 2}^k) \\ = A_1(w_{n / 2}^{k - n / 2}) - w_n^{k - n /2 }A_2(w_{n / 2}^{k - n / 2})\)。

\(k - n / 2 < n / 2\),诶这个问题我刚刚不解决过了嘛!

所以我们就可以分治,先分治处理 \(A_1\),再分治处理 \(A_2\),然后就是按照上面的式子合并 \(A_1, A_2\),最后我们就可以得到啦~

时间复杂度是经典的 \(T(n) = 2T(n / 2) + n\),也就是 \(O(n\log_2 n)\)


那么怎么点值转系数呢?

下面要简单的做一个小推导 qwq

比如多项式 \(f(x) = a_0 + a_1x + a_2x^2 + a_3x^3 + \dots + a_{n - 1}x^{n - 1}\),我们刚刚傅里叶变换出来了 \(\{f(w_n^0), f(w_n ^ 1), \dots, f(w_n ^ {n - 1})\}\),我们记它们为 \(\{b_0, b_1, b_2, \dots, b_{n - 1}\}\)。

然后我们新构造一个多项式 \(g(x) = b_0 + b_1x + b_2x^2 + b_3x^3 + \dots + b_{n - 1}x^{n - 1}\),然后我们将 \(w_n^k\) 的倒数 \(w_n^{-k}\) 弄出来代入得到 \(\{g(w_n^0), g(w_n ^ {-1}), \dots, g(w_n ^ {1-n})\}\)(这些倒数很显然满足折半和消去定理),我们记它为 \(\{c_0, c_1, c_2, \dots, c_{n - 1}\}\)。傅里叶:有完没完啊啊啊啊啊

然后我们开始推导 $c_k = g(w_{n}^{-k}) = \sum\limits_{i = 0}^{n - 1}b_i w_{n}^{-ik} \ = \sum\limits_{i = 0}^{n - 1}((\sum\limits_{j= 0}^{n - 1}a_j w_n ^ {ij})w_{n}^{-ik}) \ = \sum\limits_{i = 0}^{n - 1}(\sum\limits_{j= 0}^{n - 1}a_j w_n ^ {(j - k)i}) \ = \sum\limits_{j = 0}^{n - 1} a_j(\sum\limits_{i= 0}^{n - 1}w_n ^ {(j - k)i}) $

当 \(j = k\) 时,\(\sum\limits_{i= 0}^{n - 1}w_n ^ {(j - k)i} = \sum\limits_{i= 0}^{n - 1}w_n ^ 0 = n\)

当 \(j \not = k\) 时,\(\sum\limits_{i= 0}^{n - 1}w_n ^ {(j - k)i}\) 这一坨实际上是一个首项为 \(1\),公比为 \(w_{n}^{j - k}\) 的等比数列!

所以 \(\sum\limits_{i= 0}^{n - 1}w_n ^ {(j - k)i}) = \dfrac{w_n^{n(j - k)} - 1}{w_n^{j - k} - 1} = \dfrac{1^{(j - k)} - 1}{w_n^{j - k} - 1} = 0\)

所以 \(c_k = na_k\),即 \(a_k = \dfrac{c_k}{n}\)。

等等!我们似乎找到了系数和点值的关系!

这就是 FFT 还原的方法

先对 \(f\) 做一遍插值,再用插值得到的点值为系数,构造一个新的多项式 \(g\),代入单位根的倒数 \(w_n^0, w_n^{-1}, \dots, w_n^{1 - n}\) 到 \(g\) 里面去,再做一遍插值。所得的点值除以 \(n\) 就是 \(f\) 的各项系数


还有优化,明天填坑 qwq

标签:dots,limits,sum,FFT,笔记,学习,我们,2k
From: https://www.cnblogs.com/thirty-two/p/17480955.html

相关文章

  • SpringSecurity6.0学习常见问题
    环境SpringSecurity6.1版本SpringBoot3.1版本常见问题oauth2客户端请求oauth授权端,响应401检查spring.security.oauth2.client.registration.login-client.client-secret的值很spring.security.oauth2.authorizationserver.client.login-client.registration.client-secret......
  • JavaScript学习笔记 - 语法篇 - 一句废话没有版
    写在前面:绝不废话!放心食用JavaScript语法很简单,可以直接在控制台调试理解目录1、变量和常量2、数据类型3、字符串3.1模板字符串3.2字符串的部分常用函数4、数组5、对象6、对象数组&&JSON7、if条件&&三目运算7.1if条件7.2三目运算8、switch9、循环9.0准备工作9.1......
  • python偏函数学习笔记
    Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partialfunction)比如,int函数默认十进制转换,若提供其它base参数,就可以进行n进制转换int('12345',base=8)5349int('12345',16)74565定义一个int2()的函数,默认把base=2传进去defint2(x,base=2):retu......
  • python高阶函数filter、sorted学习笔记
    filterPython内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。e.g在一个list中,删掉偶数,只保留奇数,可以这么写:点击查看代码de......
  • 关于服务器的一些笔记
    //查看端口占用netstat-anp|grep8080//查看占用8080端口的进程:fuser-v-ntcp8080//杀死指定进程kill-s91154//持久化运行jar包nohupjava-jargdcx-web-1.0.0.jar& 内网穿透工具https://dashboard.cpolar.com/login......
  • python匿名函数学习笔记
    当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。list(map(lambdax:x*x,[1,2,3,4,5,6,7,8,9]))[1,4,9,16,25,36,49,64,81]由此,匿名函数lambdax:x*x实际上就是:deff(x):returnx*x关键字lambda表示匿名函数,冒号前......
  • Linux内核学习-通知链
    前言内核中有许多子系统,他们相互独立,但又具有很强的依赖性。因此其中一个子系统侦测到的或者产生的事件其他子系统可能都有兴趣,那么为了实现这样的交互需求,Linux使用了所谓的通知链(notificationchain)。本博客包含的主要内容1.通知链如何声明以及内核代码定义了那些链(chain)。2.内核......
  • VBA开发资料 Excel开发资料大全 VBA开源资料 VBA实战开发例子 VBA学习入门到提高 VBA
    记得十多年前还专门做个VBA开发的岗位,开发一些辅助制造业生产需要的业务,生产数据进出料,与供应商对接数据等等。现在网上招VBA的岗位少了,可能说明已经被一部分软件替代,也说明现在很多人已经能使用VBA了,可能就不专门设置这个岗位了。但在实际工作当中,使用VBA非常多的,并且快......
  • 系统架构设计师笔记第14期:系统分析与设计
    面向对象的方法面向对象方法(Object-orientedmethods)是一种软件开发方法,其核心思想是将软件系统建模为对象的集合,这些对象之间通过消息传递进行交互。面向对象方法强调对象的概念、封装、继承和多态等特性,以实现软件系统的可重用性、可维护性和灵活性。以下是面向对象方法的一些关......
  • 作为软件测试人员需要学习哪些技能和知识?
    如果您正在寻找一个快节奏的职业,在计算机领域拥有广阔的前景,那么软件测试可能是您想要追求的职业之一。随着全球数字化的浪潮,软件开发变得越来越重要,而软件测试就是确保这些应用程序和软件产品在上市前能够运行良好的关键部分。那么,作为软件测试人员,您需要学习哪些技能和知识才能做......