首页 > 其他分享 >快速傅里叶变换总结

快速傅里叶变换总结

时间:2025-01-17 20:33:52浏览次数:1  
标签:总结 10 ch 变换 omega int comp 傅里叶 2k

基本概念

对于求和式 \(\sum a_ix^i\),如果是有限项相加,称为多项式,记作

\[f(x)=\sum_{i=0}^n a_ix^i。 \]

其中最高次项的次数为 \(n\),为 \(n\) 次多项式。

用 \(n+1\) 个点可以唯一地确定一个 \(n\) 次多项式,这一过程可以参考 拉格朗日插值

引入

给定多项式 \(f(x),g(x)\),求 \(f(x)\cdot g(x)\)各项系数。

通过系数表达式直接乘时间复杂度 \(\Theta(n^2)\),但考虑两个函数的一组点值表达式

\[(x_1,f(x_1))\cdots(x_k,f(x_k))\\ (x_1,g(x_1))\cdots(x_k,g(x_k)) \]

此时两者相乘的点值表达式可以在 \(\Theta(n)\) 的时间求出。

即:

\[(x_1,f(x_1)\cdot g(x_1))\cdots(x_k,f(x_k)\cdot g(x_k)) \]

于是就有想法:将系数表达式 \(\Rightarrow\) 点值表达式,点值相乘后 \(\Rightarrow\) 系数表达式。

快速傅里叶变换就是完成中间过程的工具,可以在 \(\Theta(n \log n)\) 的时间完成系数表示与点值表示的转换。

前置知识

复数

有以下重要知识

欧拉公式:

\[\mathrm{e}^{\mathrm{i}x}=\cos x+\mathrm{i}\sin x \]

单位复数根:

\[\begin{aligned} \omega_n^k&= \cos \frac{2 \pi k}{n} + \mathrm{i} \sin \frac{2 \pi k}{n} \\ \omega_n^n&=1\\ \omega_n^k&=\omega_{2n}^{2k}\\ \omega_{2n}^{k+n}&=-\omega_{2n}^k\\ \end{aligned} \]

快速傅里叶变换

将多项式 \(f(x)=\sum_{i=0}^{n}a_ix^i\) 按照下标为奇偶数划分为两个多项式

\[G(x)=a_0+a_2{x^1}+a_4{x^2}+\dots+a_{n-2}x^{\frac{n}{2}-1}\\ H(x)=a_1+a_3{x}+a_5{x^2}+ \dots+a_{n-1}x^{\frac{n}{2}-1}\]

有:

\[f(x)= G(x^2) + xH(x^2) \]

代入两组值:
得到:

\[\begin{aligned} f(\omega_n^k) &= G((\omega_n^k)^2) + \omega_n^k \times H((\omega_n^k)^2) \\ &= G(\omega_n^{2k}) + \omega_n^k \times H(\omega_n^{2k}) \\ &= G(\omega_{n/2}^k) + \omega_n^k \times H(\omega_{n/2}^k) \end{aligned} \\ \begin{aligned} f(\omega_n^{k+n/2}) &= G(\omega_n^{2k+n}) + \omega_n^{k+n/2} \times H(\omega_n^{2k+n}) \\ &= G(\omega_n^{2k}) - \omega_n^k \times H(\omega_n^{2k}) \\ &= G(\omega_{n/2}^k) - \omega_n^k \times H(\omega_{n/2}^k) \end{aligned} \]

发现只要求出 \(G(\omega_n^{2k}),H(\omega_{n/2}^k)\) 的值就可以同时求得 \(f(\omega_n^k),f(\omega_n^{k+n/2})\) 的值。

并且这个问题跟原问题是相同的。

于是分治下来解决子问题,最后合并为原问题即可。

void FFT(comp f[],int n)
{
    if(n==1) return;
    comp f1[n/2],f2[n/2];
    for(int i=0;i<n/2;++i)
    {
        f1[i]=f[i<<1],f2[i]=f[i<<1|1];
    }
    FFT(f1,n/2),FFT(f2,n/2);
    comp w=polar(1.0,(2.0*pi/n)),wk=comp(1,0);
    for(int i=0;i<n/2;++i)
    {
        f[i]=f1[i]+f2[i]*wk;
        f[i+n/2]=f1[i]-f2[i]*wk;
        wk*=w;
    }
}

快速傅里叶逆变换

现在完成了系数表示法 \(\Rightarrow\) 点值表示法的过程,现在考虑逆向转换。

有结论:

IFFT等价于 FFT 中代入得复数变为其倒数,再除以变换长度。

证明

故结论成立。

容易证明,这也等价于将\(\{y_0,y_1,y_2,\cdots,y_{n-1}\}\) 做 FFT 变换后除以 \(n\),再反转后 \(n - 1\) 个元素。

优化

以上的代码实现都基于递归,常数较大。

基于以下观察,可以写出迭代写法。

1

原位置对应元素的下表二进制翻转后,成了最后当前元素的下标,于是这样处理之后直接自底向上合并即可。

代码

递归版本

#include <bits/stdc++.h>

using namespace std;

const int INF=0x3f3f3f3f;
inline int read()
{
    int w=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+(ch^48);
        ch=getchar();
    }
    return w*f;
}
inline void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
#define comp complex<double>
const int maxn=2e6+10;
const double pi=3.1415926535;
void FFT(comp f[],int n)
{
    if(n==1) return;
    comp f1[n/2],f2[n/2];
    for(int i=0;i<n/2;++i)
    {
        f1[i]=f[i<<1],f2[i]=f[i<<1|1];
    }
    FFT(f1,n/2),FFT(f2,n/2);
    comp w=polar(1.0,(2.0*pi/n)),wk=comp(1,0);
    // comp w=comp(cos(2.0*pi/n),type*sin(2.0*pi/n)),wk=comp(1,0);
    for(int i=0;i<n/2;++i)
    {
        f[i]=f1[i]+f2[i]*wk;
        f[i+n/2]=f1[i]-f2[i]*wk;
        wk*=w;
    }
}
void IFFT(comp f[],int n)
{
    FFT(f,n);
    reverse(f+1,f+n);
}
int n,m;
comp a[maxn<<1],b[maxn<<1];

int main()
{
    #ifndef ONLINE_JUDGE
    //freopen("in.txt","r",stdin);
    #endif
    n=read(),m=read();
    for(int i=0;i<=n;++i) a[i]=comp(read(),0);
    for(int i=0;i<=m;++i) b[i]=comp(read(),0);
    int k=1;
    while(k<=n+m) k<<=1;
    // cout<<k<<endl;
    FFT(a,k),FFT(b,k);
    for(int i=0;i<=k;++i)
    {
        a[i]*=b[i];
    }
    IFFT(a,k);
    for(int i=0;i<=n+m;++i)
    {
        cout<<(int)(a[i].real()/k+0.5)<<" ";
    }
    return 0;
}

迭代版本

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
    int w=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+(ch^48);
        ch=getchar();
    }
    return w*f;
}
void write(int x)
{
    if(x<0) x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
#define comp complex<double>

const double pi=acos(-1);
const int maxn=4e6+10;
int n,m,len,k;
int rev[maxn];
comp a[maxn],b[maxn];

int get(int x)
{
    int res=0;
    for(int i=0;i<len;++i)
    {
        res+=(x>>i&1)<<(len-i-1);
    }
    return res;
}
void fft(comp f[],int n)
{
    for(int i=0;i<n;++i)
    {
        rev[i]=get(i);
        if(i<rev[i]) swap(f[i],f[rev[i]]);
    }
    for(int h=2;h<=n;h<<=1)
    {
        comp w=polar(1.0,2.0*pi/h);
        for(int i=0;i<n;i+=h)
        {
            comp wk=comp(1,0);
            for(int j=0;j<h/2;++j)
            {
                comp x=f[i+j],y=f[i+j+h/2]*wk;
                f[i+j]=x+y,f[i+j+h/2]=x-y;
                wk*=w;
            }
        }
    }
}
void ifft(comp f[],int n)
{
    fft(f,n);
    reverse(f+1,f+n);
}

int main()
{
    n=read(),m=read();
    for(int i=0;i<=n;++i) a[i]=read();
    for(int i=0;i<=m;++i) b[i]=read();
    k=1;
    while(k<=n+m) k<<=1,++len;
    fft(a,k),fft(b,k);
    for(int i=0;i<=k;++i) a[i]*=b[i];
    ifft(a,k);
    for(int i=0;i<=n+m;++i) printf("%d ",(int)(a[i].real()/k+0.5));
    return 0;
}

解释:蝶形变换的位置数组有 \(\Theta(n)\) 的解法,但考虑 FFT 的瓶颈不在此,暴力的写法也是可行的。

标签:总结,10,ch,变换,omega,int,comp,傅里叶,2k
From: https://www.cnblogs.com/vanueber/p/18677622

相关文章

  • 2025高级java面试精华及复习方向总结
    1. Java基础顶顶顶顶的点点滴滴1.1java集合关系结构图 1.2 如何保证ArrayList的线程安全方法一:使用Collections工具类中的synchronizedList方法    List<String>synchronizedList=Collections.synchronizedList(newArrayList<>());使用锁机制     ......
  • 词性及用法总结
    在初中英语语法中,九大词性是基础且重要的语法概念,它们分别是名词、代词、动词、形容词、副词、数词、冠词、介词和连词。以下是对这九大词性的详细解释:一、名词(Nouns)定义:名词是用来命名人、地方、事物、事件或概念的词。分类:名词可以分为专有名词和普通名词。专有名词......
  • ✨️ 2024年终总结 - 23岁正是折腾的年纪 从开发到产品 | 日本旅行 | 捣鼓项目 | 技术写
    序毕业一年多了,按照大家通常采用的“四舍五入”的算法,我已经是一个有两年经验的工程师了。在写这篇24年总结的时候,先回看了一下23年的总结,不得不感叹时间真的过得好快,明明感觉大学毕业也没多久,但竟然已经是一年多前的事情了。再回看24年,在工作和生活方面也找到了一个不错的......
  • 2025北大营总结
    PKUWCDay11:00开考。先看了一眼前两题,感觉T1是思维题,T2是数据结构,T3太长了,先不看。我觉得这个T1好像小学我猜出过结论,于是果断正序开题。于是就卡了卡了。试了好几种dp都不对,甚至\(a=3\)我都不会。心态有点小崩,但是因为我知道想拿一等约必须做出来\(\ge3\)题......
  • 密码学——现代密码体制总结(别再管哈希叫加密了哦)
    文章目录加解密与现代密码体制总结一、什么是加解密?1.加密与解密2.传统的加解密(古典密码)3.现代的加解密二、现代密码体制1.现代密码的两大类2.两种体制各有特点三、对称密钥体制Ⅰ.分组密码1.Feistel——经典的分组密码结构2.DES(DataEncryptionStandard)——数......
  • 前端包管理工具npm、pnpm 和 Yarn 的总结对比
    1.npmnpm是Node.js的官方包管理工具,长期以来是JavaScript生态系统的标准工具。它提供了丰富的功能,并且与所有Node.js项目兼容。优点:广泛的兼容性:npm是默认的包管理工具,与Node.js的所有版本兼容,适用于几乎所有JavaScript项目。庞大的生态系统:由于它是Node.j......
  • 第3.5.1章 Ceres库最全总结及在机器人SLAM方面的项目应用实例
    以下是关于Ceres库的详细介绍:目录Ceres库的用途Ceres库头文件分类及应用场景Ceres库头文件中函数和类的用法及代码实例机器人SLAM方面的项目应用实例及高频函数和类Ceres库的用途Ceres库是一个开源的C++库,主要用于解决非线性最小二乘问题。它提供了一种灵活且高效......
  • [rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(04):实现窗口主题(颜色)变换(暨menu菜单
    前言本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。环境配置系统:window10平台:visualstudiocode语言:rust......
  • 拉格朗日插值总结
    问题给定\(n\)个点,确定一个多项式\(f(x)=\sum_{i=0}^{n-2}a_ix^i\)。求\(f(k)\)。解法拉格朗日插值的核心思想是通过构造\(n\)个函数,满足第\(i\)个函数经过\((x_1,0),(x_2,0),\cdots,(x_i,y_i),\cdots,(x_n,0)\),将这\(n\)个函数的系数累加即可得到原函数。具体地,有......
  • java-面试实战总结-2025-01-16
     下午接到hr电话,说是想约晚上7点的线上面试,感觉准备时间有点来不及了,我就跟hr沟通把时间改到了8点,多腾出来点时间进行复习。  招聘信息强调了要求会微服务,我这边微服务用的少,到家后就着重复习了微服务相关的知识。面试过程大概有半个小时,面试流程如下:1、开始后进行自我......