离散傅立叶变换与快速傅立叶变换(DFT与FFT)
自从去年下半年接触三维重构以来,听得最多的词就是傅立叶变换,后来了解到这个变换在图像处理里面也是重点中的重点。
本身自己基于高数知识的理解是傅立叶变换是将一个函数变为一堆正余弦函数的和的变换。而图像处理里则强调它是将图像信息从空间域往频率域转化的重要手段。最近从头学起数字图像处理,看完傅立叶变换之后,对于其中的计算方法快速傅立叶变换产生了好奇。于是搜索了下FFT,发现杭电上有几个这样的题目,其中点击率最高的是hdu1402*大数乘法。
大数乘法本来是一个n方的算法,经过FFT之后可以变为nlogn,于是看了下算法导论中多项式与FFT一节,大致弄清楚了FFT的原理和简单实现。
1.多项式的两种表示:系数表示和点对表示,两种表示之间可以互相转化,一个叫做赋值,一个叫做插值,插值是一个解带有克里蒙德行列式的过程。
2.为了让多项式乘法更快进行,可以选取一些特殊的数值作为赋值,这些特殊数值就是单位复根。
3.单位复根有相消引理和折半引理,这些可以使赋值以及插值的时间复杂度降低
4.以某个单位复根代入多项式得到的表达式就是离散傅立叶变换。(向量->数值)
5.继续分析多项式表示,可以引出一个蝴蝶操作,以及利用一个二进制平摊反转置换的预处理,使FFT可以迭代进行。
6.同时逆FFT可以采用和FFT相同的方式实现。
7.大数乘法可以看成两个多项式相乘,然后令变量为10的结果。
自学一个新的算法其实最容易的入门方式就是找一组数据试一试,在计算(1,2,3,4)的离散傅里叶变换之后自己就更清楚为什么蝴蝶操作是可以进行的。
假设两个4321相乘,那么n扩展为8
1.fft数组初始化就是(1,2,3,4,0,0,0,0)。而目的是求出()这个对应的值
2.以wn0为例:
系数形成的树如下:
第一次进行n=2的傅里叶变换之后,系数如下:
第二次进行n=4的傅里叶变换之后,系数如下:
而在这步变化里同时体现了蝶形操作:(k)与(k+n/2的关系)
hdu1402:---代码实现:
#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;
#define N 200005
#define PI acos(-1.0)
struct complex
{
double r,i;
complex(double real=0.0,double image=0.0)
{
r=real;
i=image;
}
//以下为三种虚数运算的定义
complex operator+(const complex o)
{
return complex(r+o.r,i+o.i);
}
complex operator-(const complex o)
{
return complex(r-o.r,i-o.i);
}
complex operator*(const complex o)
{
return complex(r*o.r-i*o.i,r*o.i+i*o.r);
}
}x1[N],x2[N];
char a[N/],b[N/];
int sum[N]; //结果存在sum里 void bitrev(complex *y,int l) //二进制平摊反转置换 O(logn)
{
register int i,j,k;
for(i=,j=l/;i<l-;i++)
{
if(i<j) swap(y[i],y[j]); //交换互为下标反转的元素
//i<j保证只交换一次
k=l/;
while(j>=k) //由最高位检索,遇1变0,遇0变1,跳出
{
j-=k;
k/=;
}
if(j<k) j+=k;
}
}
void fft(complex *in,int n)
{
int i,j,k;
complex u,t;
bitrev(in,n);
for(int i=;i<=n;i=i*)
{
complex wn(cos((*PI)/i),sin((*PI)/i));//初始化单位复根
for(j=;j<n;j=j+i)
{
complex w(,);
for(k=j;k<j+i/;k++)
{
u=in[k];
t=w*in[k+i/];
in[k]=u+t;
in[k+i/]=u-t;
w=w*wn;
}
}
}
}
void antifft(complex *in,int n)
{
complex x,y;
int i,j,k;
bitrev(in,n);
for(int i=;i<=n;i=i*)
{
complex init(cos((*PI*-)/i),sin((*PI*-)/i));
for(j=;j<n;j=j+i)
{
complex w(,);
for(k=j;k<j+i/;k++)
{
x=in[k];
y=w*in[k+i/];
in[k]=x+y;in[k+i/]=x-y;
w=w*init;
}
}
}
for(int i=;i<n;i++)
in[i]=in[i].r/n;
} int main() { while(~scanf("%s%s",a,b))
{
//提取系数并逆序
int na=strlen(a);
int nb=strlen(b); int n=;
//expand while(n<na* || n<nb*) n=n*; //将次数界变成2^n
for(int i=na-;i>=;i--)
{
x1[na--i].r=a[i]-'';x1[na--i].i=;
} for(int i=nb-;i>=;i--)
{
x2[nb--i].r=b[i]-'';x2[nb--i].i=;
} for(int i=na;i<n;i++)
{
x1[i].i=;x1[i].r=;
}
for(int i=nb;i<n;i++)
{
x2[i].i=;x2[i].r=;
} //分别傅里叶变换
fft(x1,n);
fft(x2,n);
//点值相乘得到新的傅里叶变换数值
for(int i=;i<n;i++)
x1[i]=x1[i]*x2[i];
//逆傅里叶变换得到系数,*10求和
antifft(x1,n);
memset(sum,,sizeof(sum));
//四舍五入
for(int i=;i<n;i++)
sum[i]=x1[i].r+0.5;
for(int i=;i<n;i++) //进位
{
sum[i+]+=sum[i]/;
sum[i]%=;
}
while(sum[n]<= && n>) n--; //检索最高位 for(int i=n;i>=;i--)
putchar(sum[i]+''); //倒序输出
printf("\n");
}
return ; }
代码参照:http://blog.csdn.net/u011328276/article/details/10020723
PS:csdn这份代码没有每次将sum初始化,有时会造成错误结果。
Accepted | 1402 | 375MS | 7492K | 3484 B | G++ |
离散傅立叶变换与快速傅立叶变换(DFT与FFT)的更多相关文章
- 为什么要进行傅立叶变换?傅立叶变换究竟有何意义?如何用Matlab实现快速傅立叶变换
写在最前面:本文是我阅读了多篇相关文章后对它们进行分析重组整合而得,绝大部分内容非我所原创.在此向多位原创作者致敬!!!一.傅立叶变换的由来关于傅立叶变换,无论是书本还是在网上可以很容易找到关于傅立叶 ...
- $\mathcal{FFT}$·$\mathcal{Fast \ \ Fourier \ \ Transformation}$快速傅立叶变换
\(2019.2.18upd:\) \(LINK\) 之前写的比较适合未接触FFT的人阅读--但是有几个地方出了错,大家可以找一下233 啊-本来觉得这是个比较良心的算法没想到这么抽搐这个算法真是将一 ...
- 快速傅立叶变换(FFT)
多项式 系数表示法 设\(f(x)\)为一个\(n-1\)次多项式,则 \(f(x)=\sum\limits_{i=0}^{n-1}a_i*x_i\) 其中\(a_i\)为\(f(x)\)的系数,用这 ...
- 快速傅立叶变换(FFT)算法
已知多项式f(x)=a0+a1x+a2x2+...+am-1xm-1, g(x)=b0+b1x+b2x2+...+bn-1xn-1.利用卷积的蛮力算法,得到h(x)=f(x)g(x),这一过程的时间复 ...
- BZOJ 2194 快速傅立叶变换之二 | FFT
BZOJ 2194 快速傅立叶变换之二 题意 给出两个长为\(n\)的数组\(a\)和\(b\),\(c_k = \sum_{i = k}^{n - 1} a[i] * b[i - k]\). 题解 ...
- NVIDIA GPU的快速傅立叶变换
NVIDIA GPU的快速傅立叶变换 cuFFT库提供GPU加速的FFT实现,其执行速度比仅CPU的替代方案快10倍.cuFFT用于构建跨学科的商业和研究应用程序,例如深度学习,计算机视觉,计算物理, ...
- FFT快速傅立叶变换的工作原理
实数DFT,复数DFT,FFTFFT是计算DFT的快速算法,但是它是基于复数的,所以计算实数DFT的时候需要将其转换为复数的格式,下图展示了实数DFT和虚数DFT的情况,实数DFT将时域中N点信号转换 ...
- 【CodeVS 3123】高精度练习之超大整数乘法 &【BZOJ 2197】FFT快速傅立叶
第一次写法法塔,,,感到威力无穷啊 看了一上午算导就当我看懂了?PS:要是机房里能有个清净的看书环境就好了 FFT主要是用了巧妙的复数单位根,复数单位根在复平面上的对称性使得快速傅立叶变换的时间复杂度 ...
- BZOJ_2179_FFT快速傅立叶_(FFT)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2179 超大整数乘法 分析 FFT模板题. 把数字看成是多项式,x是10.然后用FFT做多项式乘 ...
随机推荐
- Leetcode解题记录
尽量抽空刷LeetCode,持续更新 刷题记录在github上面,https://github.com/Zering/LeetCode 2016-09-05 300. Longest Increasi ...
- 面向对象之静态方法(static)和实例化方法的区别
这是一个经常被时时提出来的问题,很多时候我们以为理解了.懂了,但深究一下,我们却发现并不懂. 方法是我们每天都在写得,很多程序员大多都使用实例化方法,而很少使用静态方法,问原因也说不出来所以然,或者简 ...
- <META http-equiv=Content-Type content="text/html; charset=gb2312">
META,网页Html语言里Head区重要标签之一 HTTP-EQUIV类似于HTTP的头部协议,它回应给浏览器一些有 用的信息,以帮助正确和精确地显示网页内容.常用的HTTP- EQUIV类型有: ...
- 五毛的cocos2d-x学习笔记04-触摸点
Touch position是屏幕坐标系中的点,OpenGL position是cocos2d-x用到的OpenGL坐标系上的点坐标.所以就需要将touch的坐标转换成OpenGL坐标系中的点坐标. ...
- JavaSE学习总结第07天_面向对象2
07.01 成员变量和局部变量的区别 1.在类中的位置不同 成员变量 类中方法外 局部变量 方法内或者方法声明上 2.在内存中的位置不同 成员变量 堆内存 局部变量 栈内存 3 ...
- MySql 小问题集合
- 使用MySql通过SpringFramework来自动建表, 服务器用的是Tomcat, 在server.xml和context.xml中均正确配置了jdbc datasource. 编译通过, ...
- XWalkView+html 开发Android应用
在Android开发中有时候为了开发简洁和方便移植,采用了Html+WebView的开发模式,然而Android自带的WebView控件是调用的本机的浏览器内核,有些版本较老的手机浏览器和手机性能都不 ...
- C中的宏
1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)] #define 标识符替换列表 替换列表是一系列的C语言记号,包括标识符.关键字.数.字符常量.字符串字面量.运算符和 ...
- C陷阱与缺陷(二)
第二章 语法陷阱 2.1 理解函数声明 (*(void(*)())0)();任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符.一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转 ...
- 最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现(C/C++)
Dijkstra算法 ———————————最后更新时间:2011.9.25———————————Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径. ...