FFT算法学习笔记

写在前边

  1.辣鸡RRRR_wys之前csdn的博客,千年不更。。。还很水。。。于是开了这个Blog。。。妄图拯救一下自己

  2.最近接触了一些多项式理论。于是翘掉了愉快的高频自控,通过《算导》稍稍学习了一下

  3.算法竞赛中,FFT主要解决多项式的乘法等问题

FFT基本概念

  1.FFT即快速傅里叶变换, 是离散傅里叶变换的加速算法。可以在o(nlogn)的时间内,完成DFT和DFT-1

  2.DFT即离散傅里叶变换, 这里主要就是多项式的系数向量转换成点值表示的过程

多项式的表示

  1.系数表达:对于一个次数上界为n的多项式 A(x)=∑(j=0 - n-1) aj*xj 而言,系数表达式便是由系数组成的向量a = (a0, a1, a2, a3, ... , an-1)

         (1)计算A(x0)的值即:A(x0) = a0+x0(a1+x0(a2+x0(a3+x0(a4+...+x0(an-2+x0*an-1))...)))  时间为o(n)

    (2)通过系数向量计算两个多项式的乘法:设输出向量为c, 而a,b长度n=max(na,nb)那么显然两个c的长度为2n-1,计算方法为:cj = ∑(k=0 ~ j) ak*bj-k

        即向量a,b的卷积

  2.点值表达式:对于一个次数上界为n的多项式 A(x)=∑(j=0 ~ n-1) aj*xj 而言,点值表达式便是由n个点值所构成的集合{ (x0,y0), (x1,y1), (x2,y2), ... , (xn-1,yn-1) }

      (1)运用之前提到的方法,一个个计算点值时间为o(n2),之后可以看到如果我们巧妙地选取点xk可以使时间变为o(nlogn)

      (2)将点值表达式转换为系数表达式:将已知的点值带入,多项式可已得到一个有n个未知数的n个线性方程

     a0 + a1*x0 + a2*x02 + a3* x03 + ... + an-1*x0n-1 = y(1)

     a0 + a1*x1 + a2*x12 + a3* x13 + ... + an-1*x1n-1 = y(2)

     .......

     a0 + a1*xn-1 + a2*xn-12 + a3* xn-13 + ... + an-1*xn-1n-1 = yn-1 (n)

      可以写成矩阵形式,既为一个范德蒙矩阵,通过线代知识解这个方程就可以求出系数向量,复杂度o(n3)。另一种方法是采用拉格朗日插值法求解,复杂度o(n2)。因此,n个点的求值运算和插值运算,是定义完备的互逆运算。

    (3)通过点值计算两个多项式的乘积,c = { (x0,yA0*yB0), (x1,yA1*yB1), (x2,yA2*yB2), ... , (xn-1,yA(n-1)*yB(n-1)) }, 可以看到复杂度为o(n)

     (4)计算多项式乘法的过程:1)o(nlogn)分别求出两个多项式的点值表达式 2)点值乘法o(n) 3)插值o(nlogn) 4)求出答案多项式的系数表达式

DFT与FFT

  下面开始讨论,如何在o(nlogn)的时间内,完成求值和插值运算

  1.单位复数根

      n次单位复数根满足ωn=1的复数ω,n此单位复数根的数目恰好有n个,这些根是:ωnk = e2πik/n 这n个根均匀的分布在复平面上,其他n次单位根都是ωn的幂次。n个n次单位根在乘法意义下形成一个群,该群与模n意义下的加法群,有相同的结构,即ωni ωnj = ωn(i + j) mod n

   (1) 消去引理:ωndkd nk  (n>=0, k>=0, d>0)

   (2) ωnn/2 2 =-1

   (3) 折半引理:如果n>0为偶数,那么n个n次单位复数根的平方的集合就是n/2个n/2次单位复数根的集合

   (4) 求和引理:(j=0 ~ n-1) nk)j = 0 (n>=1, 不能被n整除的非负整数k)

  2.DFT

  我们希望计算ωn0, ωn1, ωn2, .... , ωnn-1处的值(在多项式乘法中其实是2n个点值)。假设给定系数a=(a0, a1, a2, ... , an-1), 即对于k=0, 1, 2, 3, ... , n-1求出yk = A(ωnk),记向量y=(y1, y2, y3, ... ,yn-1)就是系数向量a的离散傅里叶变换DFT,记为 y = DFTn(a)

  3. FFT

  通过快速傅里叶变换FFT,利用单位复数根的性质,我们可以在o(nlogn)的时间内计算DFTn(a)。首先假设次数n恰好是2的整数幂,不足在高次位置项添0。

  定义两个新的次数界为n/2的多项式 A[0] = a0 + a2*x + a4*x2 + a6* x3 + ... + an-2*xn/2-1 , A[1] = a1 + a3*x + a5*x2 + a7* x3 + ... + an-1*xn/2-1

  A[0]包含所有偶数下标的系数,A[1]包含所有奇数下标的系数,于是有A(x) = A[0](x2) + x*A[1](x2) 。

  所以,求解A(x)的DFN,转换为了求解多项式A[0], A[1]n0)2, (ωn1)2, (ωn2)2 , ... , (ωnn-1)2 处的取值。

  根据折半引理,n0)2, (ωn1)2, (ωn2)2 , ... , (ωnn-1)2 并不是由n个不同的值组成,而是由n/2个n/2次单位根复数根所组成,每个根正好出现两次。因此,我们递归的对次数界为n/2的多项式A[0], A[1]在n/2个n/2次单位复数根处进行求值。这样我们求出了次数界为n的次多项式在n次单位复数根处的值。

  注意到除了递归调用的时间外,每次所需时间为o(n),其中n是输入向量的长度。因此有如下递归式:T(n) = 2T(n/2) + o(n) = o(nlogn),时间为o(nlogn)。

  计算在单位复数根处插值,求解系数向量。根据点值表达式,我们可以列出线性方程组:

  a0 + a1 + a2 + a3+ ... + an-1 = y0 (1)

  a0 + a1* n)1+ a2*n)2 + a3* n)3 + ... + an-1*n)n-1 = y(2)

  a0 + a1n)2+ a2*n)4 + a3n)6 + ... + an-1*n)2(n-1)  =  y2  (3)

  ......

  a0 + a1n)n-1+ a2*n)2(n-1) + a3n)3(n-1) + ... + an-1*n)(n-1)(n-1)  =  yn-1 (4) 

  根据这个线性方程组,我们可以令单位复数根组成的系数矩阵为Vn,运用yVn-1求解系数。可以证明:Vn-1的(j, k)处的值为n)-kj/n  (j,k = 0, 1, 2, ... , n-1)

  有了上面的式子我们能推导出DFT-1aj =(1/n)* ∑(k=0 ~ n-1) ykn)-kj  (j = 0, 1, 2, ... , n-1)

  观察可得,相对于DFT,我们把a与y替换,用ωn-1代替ωn,并将每个元素除以n。这样,我们也可以在o(nlogn)的时间内计算出DFT-1

  卷积定理:a(卷积)b = DFT2n-1( DFT2n(a)*DFT2n(b) ) , 其中向量a, b用0填充使其长度达到2n。

[UR#34]多项式乘法

递归实现:参考hzwer的代码

#include <bits/stdc++.h>
#define pi acos(-1.0)
typedef long long ll;
using namespace std;
int n,m;
complex<double> a[300000],b[300000];
void fft(complex<double> *x, int n, int ty){if(n==1)return;complex<double> l[n>>1],r[n>>1];for(int i=0;i<=n;i+=2)l[i>>1]=x[i],r[i>>1]=x[i+1];fft(l,n>>1,ty);fft(r,n>>1,ty);complex<double> wn(cos(2*pi/n),sin(ty*2*pi/n)),w(1,0),t;for(int i=0;i<n>>1;i++,w*=wn)t=w*r[i],x[i]=l[i]+t,x[i+(n>>1)]=l[i]-t;
}
int main()
{scanf("%d%d",&n,&m);for(int i=0,x;i<=n;++i)scanf("%d",&x),a[i]=x;for(int i=0,x;i<=m;++i)scanf("%d",&x),b[i]=x;m=n+m;for(n=1;n<=m;n<<=1);fft(a,n,1);fft(b,n,1);for(int i=0;i<=n;++i)a[i]=a[i]*b[i];fft(a,n,-1);for(int i=0;i<=m;++i)printf("%d ",(int)(a[i].real()/n+0.5));return 0;
}

  

  然而,实际应用中,需要算法尽量地快速,因此,我们引出一种小常数FFT的迭代实现。

高效FFT实现

  1.蝴蝶操作

    for(int i=0;i<n>>1;i++,w*=wn)t=w*r[i],x[i]=l[i]+t,x[i+(n>>1)]=l[i]-t;

  2.考虑递归调用的过程,将每次输入向量构成一颗树,类似于zkw线段树,如果可以实现自底向上更新,相比递归常数就很优秀了。

    (1)首先,我们成对的取出输入向量树最底层的元素,利用蝴蝶操作计算出每对的DFT,这样我们就有了n/2个二元素DFT,同理,下一步取出这n/2个DFT,计算四元素DFT,最终形成n元素DFT

    (2)那么如何确定最底层的元素排列呢?尝试一下就能发现,元素出现的顺序就是是一个位逆序置换,即:n = 8时,{0, 4, 2, 6, 1, 5, 3, 7}, 写成二进制形式,即{000, 100, 010, 110, 001, 101, 011, 111},将所有元素二进制位逆序可得{000, 001, 010, 011, 100, 101, 110, 111} = {0, 1, 2 ,3, 4, 5, 6, 7}

                                                                                        

 

[UR#34]多项式乘法

迭代实现:参考hzwer的代码

#include <bits/stdc++.h>
#define pi acos(-1.0)
typedef complex<double> E;
using namespace std;
int n,m,L,R[300000];
E a[300000],b[300000];
void fft(E *a,int f){for(int i=0;i<n;++i)if(i<R[i])swap(a[i],a[R[i]]);for(int i=1;i<n;i<<=1){E wn(cos(pi/i),f*sin(pi/i));for(int p=(i<<1),j=0;j<n;j+=p){E w(1,0);for(int k=0;k<i;++k,w*=wn){E x=a[j+k],y=w*a[j+k+i];a[j+k]=x+y;a[j+k+i]=x-y;}}}
}
int main()
{scanf("%d%d",&n,&m);for(int i=0,x;i<=n;++i)scanf("%d",&x),a[i]=x;for(int i=0,x;i<=m;++i)scanf("%d",&x),b[i]=x;m=n+m;for(n=1;n<=m;n<<=1)L++;for(int i=0;i<n;++i)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));fft(a,1);fft(b,1);for(int i=0;i<=n;++i)a[i]=a[i]*b[i];fft(a,-1);for(int i=0;i<=m;++i)printf("%d ",(int)(a[i].real()/n+0.5));return 0;
}

 

NTT(Number Theory Transform)

  可是,复数运算的常数依然是很巨大的,而且精度不高。当题目给的模数很特殊的时候,我们就可以利用数论变换来加速算法。

  

 

转载于:https://www.cnblogs.com/RRRR-wys/p/8868386.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/320850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

System.IO.Pipelines: .NET高性能IO

本文翻译自dotnet团队博客文章&#xff1a;https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/ System.IO.Pipelines是一个新的库&#xff0c;旨在简化在.NET中执行高性能IO的过程。它是一个依赖.NET Standard的库&#xff…

.NET+PostgreSQL实践与避坑指南

简介.NETPostgreSQL(简称PG)这个组合我已经用了蛮长的一段时间&#xff0c;感觉还是挺不错的。不过大多数人说起.NET平台&#xff0c;还是会想起跟它“原汁原味”配套的Microsoft SQL Server(简称MSSQL)&#xff0c;其实没有MSSQL也没有任何问题&#xff0c;甚至没有Windows Se…

Jimu : .Net Core 分布式微服务框架介绍

一、前言近些年一直浸淫在 .Net 平台做企业应用开发&#xff0c;用过的 .Net 框架不多&#xff08;具体数量不清&#xff0c;印象深刻的有 Asp.Net MVC&#xff0c;WPF&#xff0c;其他很多都是基于微软开发的框架做些封装而形成新的框架&#xff0c;大都是还没起好名就湮灭在历…

.NetCore2.1 WebAPI 根据swagger.json自动生成客户端代码

前言上一篇博客中我们可以得知通过Swagger插件可以很方便的提供给接口开发者在线调试&#xff0c;但是实际上Swagger附带的功能还有很多&#xff0c;比如使用NSwag生成客户端调用代码&#xff0c;进一步解放接口开发者。NSwag NSwag是一个发布在GitHub上的开源项目&#xff0c;…

使用.NetCore 控制台演示 熔断 降级(polly)

1、熔断降级的概念&#xff1a; 熔断&#xff1a;我这里有一根长度一米的钢铁&#xff0c;钢铁的熔点1000度&#xff08;假设&#xff09;&#xff0c;现在我想用力把这根钢铁折弯&#xff0c;但是人的力有限达不到折弯的点&#xff0c;然后我使用火给钢铁加热&#xff0c;每隔…

给正在努力的您几条建议(附开源代码)

前言我是一名在广州的某家互联网公司工作&#xff0c;并有6年工作经验&#xff0c;奔着架构师与微软MVP为目标的老鸟程序员。最近回顾了下多年来走的路&#xff0c;有不少的弯路。今天不说技术&#xff0c;而是总结了一些职业生涯上的建议与大家分享。虽说今天不说技术&#xf…

.Net Core Cors中间件解析

同源策略和资源跨域共享1、同源策略同源策略&#xff0c;它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指&#xff0c;域名&#xff0c;协议&#xff0c;端口相同。1.1、目的主要是为了保证用户信息的安全&#xff0c…

用Way.EntityDB进行Entity Framework Core数据库建模

Way.EntityDB是一个基于EF Core的数据层框架&#xff0c;它取消了EF Core的Migration机制&#xff0c;因为Migration并不是通用的&#xff0c;比如说sql server生成的migration&#xff0c;如果换成sqlite&#xff0c;运行时会报错的&#xff0c;也就是数据库不能更换。Way.Ent…

.NET Core开发日志——Runtime IDentifier

.NET Core对于传统.NET开发人员而言是既熟悉又陌生的新平台&#xff0c;所以有时遇上出乎意料的事情也纯属正常情况。这时只需点耐心&#xff0c;多查查资料&#xff0c;努力找到原因&#xff0c;也未尝不是件有意义的体验。比如当建完一个最简单的控制台应用程序&#xff1a;d…

Full_of_Boys训练5总结

题目来源&#xff1a;2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest A. Advertising Strategy 贪心方法&#xff1a;把一部分k放到初始值&#xff0c;剩下一部分&#xff0c;等到最后用。然后&#xff0c;枚举第一部分放多少即可。 #include <bits/stdc.h> typ…

C#语法——await与async的正确打开方式

C#5.0推出了新语法&#xff0c;await与async&#xff0c;但相信大家还是很少使用它们。关于await与async有很多文章讲解&#xff0c;但有没有这样一种感觉&#xff0c;你看完后&#xff0c;总感觉这东西很不错&#xff0c;但用的时候&#xff0c;总是想不起来&#xff0c;或者不…

好代码是管出来的——.Net Core集成测试与数据驱动测试

软件的单元测试关注是的软件最小可执行单元是否能够正常执行&#xff0c;但是软件是由一个个最小执行单元组成的集合体&#xff0c;单元与单元之间存在着种种依赖或联系&#xff0c;所以在软件开发时仅仅确保最小单元的正确往往是不够的&#xff0c;为了保证软件能够正确运行&a…

【高精】【快速幂】穿越丛林(ssl 2314)

穿越丛林 ssl 2314 题目大意&#xff1a; 求2n2^n2n 原题&#xff1a; 题目描述&#xff1a; ljj 是一位富有冒险心又很喜欢研究数学的孩纸&#xff0c;有一天&#xff0c;他到一个丛林冒险&#xff0c;这里的树长有像0、4、6、8、9这样形状的洞&#xff0c;他要想穿过丛…

谈谈surging引擎的tcp、http、ws协议和如何容器化部署

1、前言分布式已经成为了当前最热门的话题&#xff0c;分布式框架也百花齐放&#xff0c;群雄逐鹿。从中心化服务治理框架&#xff0c;到去中心化分布式服务框架&#xff0c;再到分布式微服务引擎&#xff0c;这都是通过技术不断积累改进而形成的结果。esb,网关&#xff0c;ngi…

Helm - Kubernetes服务编排的利器

Helm介绍在Kubernetes中部署容器云应用&#xff08;容器或微服务编排&#xff09;是一项有挑战性的工作&#xff0c;Helm就是为了简化在Kubernetes中安装部署容器云应用的一个客户端工具。通过Helm能够帮助开发者定义、安装和升级Kubernetes中的容器云应用。同时&#xff0c;也…

.NET Core微服务之基于MassTransit实现数据最终一致性(Part 1)

一、预备知识&#xff1a;数据一致性关于数据一致性的文章&#xff0c;园子里已经有很多了&#xff0c;如果你还不了解&#xff0c;那么可以通过以下的几篇文章去快速地了解了解&#xff0c;有个感性认识即可。&#xff08;1&#xff09;左正&#xff0c;《保证分布式系统数据一…

Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)

写在前面本文地址&#xff1a;http://www.cnblogs.com/yilezhu/p/9315644.html作者&#xff1a;yilezhu上一篇关于Asp.Net Core Web Api图片上传的文章使用的是mongoDB进行图片的存储&#xff0c;文章发布后&#xff0c;张队就来了一句&#xff0c;说没有使用GridFS。的确博主只…

.NET Core开发日志——从ASP.NET Core Module到KestrelServer

ASP.NET Core程序现在变得如同控制台(Console)程序一般&#xff0c;同样通过Main方法启动整个应用。而Main方法要做的事情很简单&#xff0c;创建一个WebHostBuilder类&#xff0c;调用其Build方法生成一个WebHost类&#xff0c;最后启动之。实现代码一目了然&#xff1a;要想探…

【bfs】极其简单的最短路问题

极其简单的最短路问题 题目大意&#xff1a; 求最短路&#xff0c;权值只有1或2 原题&#xff1a; 题目描述 小C终于被小X感动了&#xff0c;于是决定与他看电影&#xff0c;然而小X距离电影院非常远&#xff0c;现在假设每条道路需要花费小X的时间为1&#xff0c;由于有数…

GraphQL 的前世今生

GraphQL是什么GraphQL是一种新的API标准&#xff0c;它提供了一种更高效、强大和灵活的数据提供方式。它是由Facebook开发和开源&#xff0c;目前由来自世界各地的大公司和个人维护。GraphQL本质上是一种基于api的查询语言&#xff0c;现在大多数应用程序都需要从服务器中获取数…