幂级数与傅里叶级数
幂级数
称
收敛半径与收敛区间
则
收敛区间
- 若按(式1-2)
,则级数(式1-1)在区间 上绝对收敛并内闭一致收敛,且此时区间 为级数(式1-1)的收敛区间 - 若按(式1-2)
,则级数(式1-1)在区间 之外处处发散 - 若按(式1-2)
,则级数(式1-1)在 可能收敛,也可能发散
泰勒展开
设
令
等式右端恰好为
解析函数:若
泰勒余项
显然,实际泰勒展开时
- 拉格朗日形式
- 柯西形式
- 积分形式
傅里叶级数
三角级数
称
傅里叶系数与傅里叶级数
设
(式2-2)称
相应地,只要
但是,此傅里叶级数是否收敛,是否以
奇函数的傅里叶级数是正弦级数,偶函数的傅里叶级数是余弦函数
任意区间上的傅里叶级数
其中,
同理,任意区间是
函数正交
从两个向量正交引申到两个函数正交(函数是无限维的向量,积分是无限微分累加),如两可积函数
则称函数
如果可积函数序列
则称函数系
显然,正弦函数与余弦函数组成的三角函数系
三角函数系可作为希尔伯特空间的基函数,幂函数系
若
复数形式的傅里叶级数
则称
对比可知,
复数形式更为优雅简洁,且当向量与旋转因子
傅里叶变换
卷积定理,时域上卷积运算对应于频域上的傅里叶变换的乘积
- 三角级数的收敛性以及和函数(如果存在)颇为复杂,无普遍解答
- 对于给任意定函数
能否展开为三角级数,可知 在 上的绝对可积,则必有傅里叶级数与之对应
傅里叶变换给出了信号(时变函数)的频率信息,也就意味着它可以告诉我们该信号中各种频率的个数,但是不能告诉我们特定频率在何时(对应的时刻)存在。
- 当信号是*稳状态时,频率对应的时刻是不需要的
- 对非*稳过程,傅里叶变换有局限性;即将一段信号正放或者反放都可以得到一些相同的傅里叶级数
将一段信号正放或反放,可以得到部分相同的傅里叶级数系数。
也就是说,信号的傅里叶级数表达式是不变的,但是信号的时域表达式会发生改变。具体解释如下:
-
对于一个周期性信号
的傅里叶级数表达式为:其中
是傅里叶级数的系数。 -
如果将信号
对称地反转,得到新的信号 。 -
将
的傅里叶级数展开,可以发现 和 的系数保持不变,只是 的系数发生改变为 。 -
因此,正放和反放信号的傅里叶级数系数是相同的,只是
项的系数发生了改变。 -
这是因为傅里叶级数表达式描述的是信号的频谱特性,而不是时域形状。
所以,正放和反放信号的傅里叶级数表达式虽然不同,但其傅里叶级数系数是相同的。这是一个重要的性质,在信号分析中经常用到。
时域上有巨大差异的信号,频谱却非常一致,频率的信号的成分是一样的,只是出现的先后顺序不同
短时傅里叶变换
为实现频域信号的时刻识别,把整个时域过程分解成无数个等长的小过程,每个小过程*似*稳,再进行傅里叶变换,就能知道在哪个时间点上出现了什么频率了。这就是短时傅里叶变换(the short-time Fourier transform,STFT)
窗函数
- 窗太窄,窗内的信号太短,会导致频率分析不够精准,频率分辨率差。
- 窗太宽,时域上又不够精细,时间分辨率低。
- 时频共轭
- 对于时变的非稳态信号,高频适合小窗口,低频适合大窗口
- STFT的窗口是固定的,在一次STFT中宽度不会变化,所以STFT还是无法满足非稳态信号变化的频率的需求
快速傅里叶变换
快速傅里叶变换(Fast Fourier Transform,FFT),是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的
#include<bits/stdc++.h>
using namespace std;
//complex是stl自带的定义复数的容器
typedef complex<double> cp;
#define N 2097153
//pie表示圆周率π
const double pie=acos(-1);
int n;
cp a[N],b[N];
int rev[N],ans[N];
char s1[N],s2[N];
//读入优化
int read(){
int sum=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
//初始化每个位置最终到达的位置
{
int len=1<<k;
for(int i=0;i<len;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数)
void fft(cp *a,int n,int flag){
for(int i=0;i<n;i++)
{
//i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(int h=1;h<n;h*=2)//h是准备合并序列的长度的二分之一
{
cp wn=exp(cp(0,flag*pie/h));//求单位根w_n^1
for(int j=0;j<n;j+=h*2)//j表示合并到了哪一位
{
cp w(1,0);
for(int k=j;k<j+h;k++)//只扫左半部分,得到右半部分的答案
{
cp x=a[k];
cp y=w*a[k+h];
a[k]=x+y; //这两步是蝴蝶变换
a[k+h]=x-y;
w*=wn; //求w_n^k
}
}
}
//判断是否是FFT还是IFFT
if(flag==-1)
for(int i=0;i<n;i++)
a[i]/=n;
}
int main(){
n=read();
scanf("%s%s",s1,s2);
//读入的数的每一位看成多项式的一项,保存在复数的实部
for(int i=0;i<n;i++)a[i]=(double)(s1[n-i-1]-'0');
for(int i=0;i<n;i++)b[i]=(double)(s2[n-i-1]-'0');
//k表示转化成二进制的位数
int k=1,s=2;
while((1<<k)<2*n-1)k++,s<<=1;
init(k);
//FFT 把a的系数表示转化为点值表示
fft(a,s,1);
//FFT 把b的系数表示转化为点值表示
fft(b,s,1);
//FFT 两个多项式的点值表示相乘
for(int i=0;i<s;i++)
a[i]*=b[i];
//IFFT 把这个点值表示转化为系数表示
fft(a,s,-1);
//保存答案的每一位(注意进位)
for(int i=0;i<s;i++)
{
//取实数四舍五入,此时虚数部分应当为0或由于浮点误差接*0
ans[i]+=(int)(a[i].real()+0.5);
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
while(!ans[s]&&s>-1)s--;
if(s==-1)printf("0");
else
for(int i=s;i>=0;i--)
printf("%d",ans[i]);
return 0;
}
小波变换
小波变换(the wavelet transform)直接把傅里叶变换的基进行替换——将无限长的三角函数基换成了有限长衰减的小波基。这样不仅能够获取频率,还可以定位到时间
某频率的基函数与信号相乘后积分,会得到一个很大的值,因为此时二者有一种重合关系——相关性。此时,就可以判断信号包含该频率的成分,本质在计算信号和三角函数的相关性
不同于傅里叶变换,变量只有频率
- 尺度
控制小波函数的伸缩,*移量 控制小波函数的*移 - 尺度
对应频率(反比),*移量 对应时间 - 小波变换可以得到一个时频谱
- 对于突变信号,傅里叶变换存在吉布斯效应,用无限长的三角函数也很难拟合好突变信号
- 只有小波函数和信号突变处重叠时,系数不为0
常用的小波函数
- Haar小波
- Morlet小波
- Marr小波
- DOG(Difference of Gaussian)小波
不同的小波在正交性、紧支撑性、*滑性和对称性上表现出不同的特性,往往难以构造一个同时具有4种特性的小波函数
多项式
有限项的幂级数称为多项式,即称
多项式的表示法
- 系数法:
- 点值法:
,由 个点构成的集合可以确定多项式方程
多项式乘积
对两个多项式
- 系数法:枚举
的每一位的系数与 的每一位的系数相乘,多项式乘法时间复杂度 - 点值法:
,复杂度只有枚举 的
朴素系数转点值的算法叫DFT(离散傅里叶变换),优化后为FFT(快速傅里叶变换),点值转系数的算法叫IDFT(离散傅里叶逆变换),优化后为IFFT(快速傅里叶逆变换)。
两函数的傅里叶变换的乘积等于它们卷积后的傅里叶变换
由于多项式乘法用点值表示比用系数表示快的多,所以我们先要将系数表示法转化成点值表示法相乘,再将结果的点值表示法转化为系数表示法的过程。
第一个过程叫做FFT(快速傅里叶变换),第二个过程叫IFFT(快速傅里叶逆变换)
DFT 离散傅里叶变换
对于任意多项式系数表示点值表示,例如
傅里叶发现,代入一些特殊点可以对过程优化
他规定了点值表示中的
复数
把上述的
不过时间复杂度是
FFT 快速傅里叶变换
对于多项式
设
考虑
这个操作叫蝴蝶变换。
而
时间复杂度
迭代FFT:
初始位置:
第一轮后:
第二轮后:
第三轮后:
每个数放到最后的位置上,然后不断向上还原,同时求出点值表示就可以
代码实现FFT
#include<bits/stdc++.h>
using namespace std;
//complex是stl自带的定义复数的容器
typedef complex<double> cp;
#define N 2097153
//pie表示圆周率π
const double pie=acos(-1);
int n;
cp a[N],b[N];
int rev[N],ans[N];
char s1[N],s2[N];
//读入优化
int read(){
int sum=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
//初始化每个位置最终到达的位置
{
int len=1<<k;
for(int i=0;i<len;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数)
void fft(cp *a,int n,int flag){
for(int i=0;i<n;i++)
{
//i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(int h=1;h<n;h*=2)//h是准备合并序列的长度的二分之一
{
cp wn=exp(cp(0,flag*pie/h));//求单位根w_n^1
for(int j=0;j<n;j+=h*2)//j表示合并到了哪一位
{ cp w(1,0);
for(int k=j;k<j+h;k++)//只扫左半部分,得到右半部分的答案
{ cp x=a[k];
cp y=w*a[k+h];
a[k]=x+y; //这两步是蝴蝶变换
a[k+h]=x-y;
w*=wn; //求w_n^k
}
}
}
//判断是否是FFT还是IFFT
if(flag==-1)
for(int i=0;i<n;i++)
a[i]/=n;
}
int main(){
n=read();
scanf("%s%s",s1,s2);
//读入的数的每一位看成多项式的一项,保存在复数的实部
for(int i=0;i<n;i++)a[i]=(double)(s1[n-i-1]-'0');
for(int i=0;i<n;i++)b[i]=(double)(s2[n-i-1]-'0');
//k表示转化成二进制的位数
int k=1,s=2;
while((1<<k)<2*n-1)k++,s<<=1;
init(k);
//FFT 把a的系数表示转化为点值表示
fft(a,s,1);
//FFT 把b的系数表示转化为点值表示
fft(b,s,1);
//FFT 两个多项式的点值表示相乘
for(int i=0;i<s;i++) a[i]*=b[i];
//IFFT 把这个点值表示转化为系数表示
fft(a,s,-1);
//保存答案的每一位(注意进位)
for(int i=0;i<s;i++) {
//取实数四舍五入,此时虚数部分应当为0或由于浮点误差接*0
ans[i]+=(int)(a[i].real()+0.5);
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
while(!ans[s]&&s>-1)s--;
if(s==-1)printf("0");
else for(int i=s;i>=0;i--)
printf("%d",ans[i]); return 0;
}
总结
-
泰勒展开的空间变化是局部的,因为它是在函数的某个点附*进行*似。这意味着,随着远离该点,*似的精度会下降。
-
傅里叶展开的空间变化是全局的,因为它在整个函数定义域内进行*似。这意味着,即使远离某个点,*似的精度也相对稳定。
最后一个问题,为什么? 为什么可以使用泰勒级数或傅里叶级数来*似拟合函数?
泰勒级数和傅里叶级数都可以用来拟合函数,背后的数学原理如下:
-
泰勒级数:
- 泰勒级数是利用函数在某一点(通常是原点)的导数来表示函数在该点附*的*似表达式。
- 根据泰勒公式,任何连续可导的函数
都可以表示为沿某点 展开的幂级数形式。这种展开形式可以用来*似表达函数。可导函数在该点处的值、一阶导数、二阶导数直到n阶导数都可以找到幂级数进行等价,那么二者在该点附*就是可以*似的 - 泰勒级数利用了函数在某点的泰勒展开,能够逼*函数在该点附*的行为。通过保留足够多的项,可以得到良好的逼*效果。
-
傅里叶级数:
- 傅里叶级数是基于周期性函数可以被一系列正弦和余弦函数的线性组合所表示的原理。
- 任何满足一定连续条件的周期性函数,都可以表示为一系列正弦和余弦函数的级数形式,这就是傅里叶级数展开。
- 傅里叶级数利用了周期函数可以被正弦和余弦函数的线性组合表示的性质。通过保留足够多的项,可以得到良好的逼*效果。
- 希尔伯特空间的完备性意味着,任何
中的函数都可以被这组标准正交基的线性组合所逼*。也就是说,任何周期性函数都可以表示为正弦和余弦函数的傅里叶级数展开形式
在希尔伯特空间
具体来说:
-
表示定义在区间 上的*方可积函数构成的希尔伯特空间。 -
*方可积函数
满足:也就是说,函数
的*方在区间 上可以积分,且积分值是有限的。 -
在这个希尔伯特空间中,函数的内积定义为:
这就给
赋予了完备的内积空间结构。 -
中的元素可以是各种周期性函数,例如连续函数、间断函数、可微函数等,只要它们的*方在 上可积即可。 -
这个希尔伯特空间为傅里叶分析奠定了基础。因为任何
中的函数都可以表示为正弦和余弦函数的傅里叶级数展开形式。
总之,
还有其他的变换,Hilbert transform、Wigner distributions、the Radon Transform、our featured transformation
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人