我在下面实现了一个简单的FFT(忽略了最终的缩放):
typedef complex<double> base;
vector<base> w;
int FFTN = 1024;
void fft(vector<base> &fa){
int n = fa.size();
if (n==1) return;
int half = (n>>1);
vector<base> odd(half),even(half);
for(int i=0,j = 0;i<n;i+=2,j++) {
even[j] = fa[i];
odd[j] = fa[i+1];
}
fft(odd);
fft(even);
int fact = FFTN/n;
for (int i=0;i<half;i++){
fa[i] = even[i] + odd[i] * w[i * fact];
fa[i + half] = even[i] - odd[i] * w[i * fact];
}
}
效果很好。但我坚持将其转换为迭代形式。到目前为止我尝试了什么:
int n = fa.size();
int fact = (FFTN>>1);
int half = 1;
while(half<n){
for(int i=0;i<n/half;i+=2){
base even = fa[i], odd = fa[i+1];
fa[i] = even + odd * w[i*fact];
fa[i+half] = even - odd*w[i*fact];
}
for(int j=0;j<n/half;j++)
fa[j] = fa[j+half];
fact >>= 1;
half <<= 1;
}
有人可以帮我解决转换问题吗?
答案 0 :(得分:1)
这是我的实施:
typedef complex<double> Data;
const double PI = acos(-1);
// Merges [low, (low + high) / 2) with [(low + high) / 2, high) parts.
void merge(vector<Data>& b, int low, int high) {
int n = high - low;
Data cur(1), mul(cos(2. * PI / n), sin(2. * PI / n));
for (int i = low; i < low + n / 2; i++) {
Data temp = b[i + n / 2] * cur;
b[i + n / 2] = b[i] - temp;
b[i] = b[i] + temp;
cur = cur * mul;
}
}
// Computes FFT for the vector b.
void do_fft(vector<Data>& b) {
int n = b.size();
int hi = 0;
while ((1 << hi) < n)
hi++;
hi--;
// Permutes the input vector in a specific way.
vector<int> p(n);
for (int i = 0; i < n; i++)
for (int b = hi; b >= 0; b--)
if (i & (1 << b))
p[i] |= (1 << (hi - b));
vector<Data> buf(n);
for (int i = 0; i < n; i++)
buf[i] = b[p[i]];
copy(buf.begin(), buf.end(), b.begin());
for (int h = 2; h <= n; h *= 2)
for (int i = 0; i < n; i += h)
merge(b, i, i + h);
}
这种实现的想法是以这样一种方式置换给定的向量,即我们需要在每一步合并相邻的子向量(即[0,0]与[1,1],[2,2]在第一步用[3,3]等,[0,1]用[2,3],[4,5]和[6,7]在第二步,等等)。事实证明,元素应该以下列方式进行置换:我们应该采用元素索引的二元表示,反转它,并将具有反向索引的元素放到当前位置。我无法严格证明这一点,但为n = 8
或n = 16
绘制小图片有助于理解它是正确的。
答案 1 :(得分:1)
这并没有完全提供解决方案。但是可能有助于解决类似问题的人将递归算法转换为迭代算法。递归在具有堆栈的系统中实现。每次对方法的递归调用都会将信息推送到堆栈上:
如果程序员可以使用stack + while loop
执行上述操作,我们可以将迭代算法实现为递归算法。步骤将是
使用上述方法进行迭代因子计算的代码示例。
int coreLogic( int current, int recursiveParameter ) {
return current * recursiveParameter ;
}
int factorial( int n ) {
std::stack<int> parameterStack ;
int tempFactorial = 1;
//parameters that would have been used to invoke the recursive call will now be pushed to stack
parameterStack.push( n );
while( !parameterStack.empty() ) {
//popping arguments from stack
int current = parameterStack.top();
parameterStack.pop();
//and invoking core logic
tempFactorial = coreLogic( tempFactorial, current );
if( current > 1 ) {
//parameters that would have been used to invoke the recursive call will now be pushed to stack
parameterStack.push( current - 1 );
}
/*
*if a divide and conquer algorithm like quick sort then again push right side args to stack
* - appers case in question
*if( condition ) {
* parameterStack.push( args );
*}
*/
}
return tempFactorial;
}
答案 2 :(得分:1)
我要做的第一件事就是让你的功能更加递归&#34;。
void fft(base* fa, size_t stride, size_t n) {
if (n==1) return;
int half = (n>>1);
fft(fa+stride, stride*2, half); // odd
fft(fa, stride*2, half); // even
int fact = FFTN/n;
for (int i=0;i<half;i++){
fa[i] = fa[stride*2*i] + fa[stride*2+i+stride] * w[i * fact];
fa[i + half] = fa[stride*2*i] - fa[stride*2+i+stride] * w[i * fact];
}
}
void fft(std::vector<base>& fa){ fft(fa.data(), 1, fa.size()); }
现在我们在缓冲区内就地fft
。
由于我们现在有两个不同的fft
实现,我们可以相互测试它们。此时构建一些单元测试,因此可以针对已知的&#34; good&#34;进行进一步的更改。 (或至少稳定的)一组行为。
接下来,我们可以检查元素在原始向量中组合的顺序。检查长度为4的缓冲区。
a b c d
我们递言做奇数甚至
a[e] b[o] a[e] d[o]
然后递归做奇数甚至
a[ee] b[oe] a[eo] d[oo]
这些集合的大小为1.它们是单独存在的,然后我们将它们组合在奇数和偶数上。
现在我们看一下8.在两次递归之后,这些元素已经拥有了#39;由:
0[ee] 1[oe] 2[eo] 3[oo] 4[ee] 5[oe] 6[eo] 7[oo]
之后3:
0[eee] 1[oee] 2[eoe] 3[ooe] 4[eeo] 5[oeo] 6[eoo] 7[ooo]
如果我们撤消这些标签,并致电e
0
和o
1
,我们会得到:
0[000] 1[001] 2[010] 3[011] 4[100] 5[101] 6[110] 7[111]
是二进制计数。第一位被丢弃,现在相等的元素在第二次到最后一次递归调用中合并。
然后丢弃前两位,并组合匹配最后一位的元素。
我们可以不用查看位,而是查看每个组合的开头和步幅。
第一个组合的步长等于数组长度(每个元素1个)。
第二个是长度/ 2。第三个是长度/ 4。
这一直持续到步幅1。
要组合的子阵列数等于步幅长度。
所以
for(size_t stride = n; stride = stride/2; stride!=0) {
for (size_t offset = 0; offset != stride; ++offset) {
fft_process( array+offset, stride, n/stride );
}
}
其中fft_process
基于:
int fact = FFTN/n;
for (int i=0;i<half;i++){
fa[i] = fa[stride*2*i] + fa[stride*2+i+stride] * w[i * fact];
fa[i + half] = fa[stride*2*i] - fa[stride*2+i+stride] * w[i * fact];
}
可能是这样的:
void fft_process( base* fa, size_t stride, size_t n ) {
int fact = FFTN/n; // equals stride I think! Assuming outermost n is 1024.
for (int i=0;i<half;i++){
fa[i] = fa[stride*2*i] + fa[stride*2+i+stride] * w[i * fact];
fa[i + half] = fa[stride*2*i] - fa[stride*2+i+stride] * w[i * fact];
}
}
这些都没有经过测试,但它提供了如何执行此操作的分步示例。您将要在此迭代版本上释放之前编写的单元测试(以测试fft
的两个早期版本)。