c2java 第9篇 FFT

傅里叶分析是什么?

设多项式A(x) = a_0 + a_1*x^1 + a_2*x^2 + ... + a_{n-1}*x^{n-1},
系数向量记为a = (a_0, a_1, ..., a_{n-1}),
多项式求值向量记为y=(y_0, y_1, ..., y_{n-1}),
y_{j} = A(w_n^j),
w_n = e^{2*\pi*i/n},
称y = FT(a)为离散傅里叶变换
反过来, 跟据y求系数a也很容易,称a=IFT(y)为逆傅里叶变换。
fft-test.c/fft_ref就是按定义来求FT的,复杂度为O(N^2)

基本原理
====
设n是2的幂。把A(x)拆分为
B(x) = a_0 + a_2 * x + ... + a_{n-2} * x ^{n/2-1}
C(x) = a_1 + a_3 * x + ... + a_{n-1} * x ^{n/2-1}
则A(x) = B(x^2) + x*C(x^2); (I)
直观看B(x), B(x)的次数为n/2, 问题规模减半,那计算量f(n)是否减半呢?
我们需要计算n点:(w_n^0)^2, (w_n^1)^2, ..., (w_n^n)^2,
由折半引理:(w_n^{k+n/2})^2 = (w_n^k)^2, 第k个点等于第n/2+k个点。
得到f(n) = 2*f(n/2) + O(n).

如果按递归写法,需要给子问题开辟系数存储所需的空间,某一时刻
最大为:n + n/2 + n/4 + ... + 2 + 1 = 2*n.
实际内存要求比这个值大,因为内存管理还有额外开销。
一定需要额外空间么,可不可以原地操作呢?

迭代版本
====
递归并不一定能转化为迭代的,有时需要我们重新定义问题和输入数据。
递归中,计算顺序是假设子问题已经算好的,因此不需要特别关;而迭代中却是首要解决的问题。
观察下面这个由递归调用形成的树。
从底到上,第二层左边第一个节点(b0, b1):
b0 = a0 + a4; b1 = a0 - a4;
因为在同一层中,没有其他地方修改或引用原来的a0, a4, 因此
我们可以直接把存储空间b0,b1 放到a0,a4。
因为我们首先是按a0,a4,a2,a6,a1,a5,a3,a7顺序计算的,因此开始时需要
把原始数据A摆成这样的顺序。
下面是把这个过程一般化:
FFT(A, bits)
arrange(A);
n = 2^bits;
for(s = 1; s <= bits; ++s){//s表示在哪一层
 m = 2^s;//当前问题的规模
 for(k = 0; k < n; k += m){//k表示从左边开始数第几个节点
  A[k,...,k+m-1] = 应用等式(I)合并
   A[k,...,k+m/2-1] 和 A[k+m/2, ..., k+m-1]
 }
}

ffmpeg 中的FFT
====
fft_template.c/fft_calc_c 的实部和虚部是放在同一个数组的两端,
比且对于n=4,n=8直接内循环展开。

SSE 3DNow向量指令优化:
x86/fft.asm
 fft_calc

应用
====
1. 多项式乘法,即卷积, 为什么卷积重要?
2. ffmpeg:wma*.c 等,DCT似乎在每个codec都有。
3. 为什么要有MDCT ?

 

代码:

 

结果:

$ make
javac FFT.java && java FFT
bits = 16
FFT 0.059000 s
Ref 47.485000 s
err max=0.000466367, avg=4.53241e-06
ludi@ubun:~/java

 

你可能感兴趣的:(java,Codec)