FFT倒位序算法的改进

前面的博文中我们给出了一个很简洁的快速傅里叶变换,然而其性能非常不佳运行缓慢,通过改进旋转因子的计算方式,我们将某些三角求解转化为复乘复加,从而减小了相当一部分计算量,性能显著上升。此时我们分析算法发现整个程序性能瓶颈在于前期的倒位序算法,普通算法将会消耗超过鲽形变换的时间,这一篇文章中我们试图在这一方面进行改进

1.对偶性

我们以 N2N2 作为分割点,设 aa 为小于 N2 的一个偶数( AaA≠a), AAa 的逆序数,可以看到有

A=rev(a)(1.a)A=rev(a)(1.a)
其中函数 rev()rev() 是一个倒位序函数。
一看这个A就是一个偶数(将之展开为加权二进制,小于 N2N2 的数字最高为权为零,倒位序以后 2020 的权为零)
同时
A=rev(a)(1.b)A=rev(a)(1.b)
也成立,我们称这个性质为对偶性。可以证明:对于任意一对原数和逆序数,对偶性始终成立

利用对偶性,我们有可能一定程度上缩小问题的规模

考虑了偶数的情况,我们再来看一下奇数,我们可以证明

rev(a+1)=N2+A(2.a)rev(a+1)=N2+A(2.a)
成立(同样展开为加权二进制的形式,最低为上的1倒位序后出现在最高位上),同样根据对偶性我们有
rev(N2+A)=a+1(2.b)rev(N2+A)=a+1(2.b)

我们还需要
rev(N2+a+1)=N2+A+1(3.a)rev(N2+a+1)=N2+A+1(3.a)
rev(N2+A+1)=N2+a+1(3.b)rev(N2+A+1)=N2+a+1(3.b)

罗列了这么多性质我们可以利用其完成需要的工作。如果不理解这四组八个小等式,不妨写一个N=16时的原序序列和逆序序列就懂啦

2.利用对偶性完成优化

这里利用对偶性我们先完成一个小目标:将运算量降低至原来的 1414
首先利用式子 1.a1.a ,计算小于 N2N2 的偶数,这里以N=16为例

0123456789101112131415
0426

这时候我们可以看到 1.b1.b 实际上没有起到多大作用(能否发挥作用在最后进行探讨)
然后看到式子 2.a2.arev(a+1)=N2+Arev(a+1)=N2+A 我们可以计算小于 N2N2 的奇数,这里A我们在上一步中已经完成计算,只需要简单地加上一个 N2N2 就可以了

0123456789101112131415
00+8=844+8=1222+8=1066+8=14

现在小于 N2N2 的部分已经完成了,根据对偶性,利用式子 2.b2.brev(N2+A)=a+1rev(N2+A)=a+1

注意在利用这个式子之前我们需要证明:在小于 N2N2 的序列中,原序偶数序列的逆序序列均小于 N2N2 ,奇数序列的逆序序列均大于 N2N2

证明非常容易,只需展开为二进制即可。现在我们可以放心使用 2.b2.b

0123456789101112131415
084122106141537

剩下的只有大于 N2N2 的奇数部分了,我们有公式 3.a3.a ,利用其我们可以顺利计算出这些剩下的部分

0123456789101112131415
0841221061418+0+1=958+4+1=1338+2+1=1178+6+1=15

我们看到对偶式 3.b3.b 没有起到多大作用
我们得到最后的逆序序列

0123456789101112131415
084122106141951331175

3.优化后的C语言实现

int BitReverse(int j)
{
    int i, p;
        p = 0;
        for (i = 0; i < LogN; i++)
        {
            if (j&(1 << i))
            {
                p += 1 << (LogN - i - 1);
            }
        }
        return p;
}

int bitcopy()
{
    int i = 0, j;
    for (i = 0; i < N / 2;i += 2)
    {
        rev[i] = BitReverse(i);
        rev[i + 1] = N / 2 + rev[i];
        rev[N / 2 + i] = rev[i] + 1;
        rev[N / 2 + i + 1] = N / 2 + rev[i] + 1;
    }
    return 0;
}

这里rev[]就是生成的倒位序列。实际上我们完全不需要存储这个倒位序列(既然要存储这个序列查表法岂不更好?),我们注意到上面的每一步仅仅是完成两个数字之间的交换,添加少量代码即可完成,这里不再赘述。


通过改进算法,在我的电脑上进行 N=221N=221 的FFT,结果时间提高到0.19s,距离FFT Benchmark中最快的ooura FFT仅仅0.05s之遥

4.随便的一点探讨

上面我们说对偶式 1.b1.b3.b3.b 似乎没有起到多大作用。这里以 1.b1.b ,当N=16时为例

01234567
0

运用对偶,0还是0额算了

01234567
042(对偶)

这里可以发挥作用了,我们可以运用对偶性省去一次计算,棒棒哒~

01234567
0426

还是他本身,运算结束。
我们发现这里用了对偶可以省去一次运算。那么对于任意的 NN1.b 可以发挥多大作用?由于求解总是由小到大,我们发现只要满足

rev(a)arev(a)>a
对偶就可以发挥作用节省运算次数,这个节省的次数是可以算出来的,我算了一下是
2logN222⌈logN2⌉−2

这个东西是递增的N越大省去的运算里越多,只不过在N较大的时候它接近线性
同样的式 3.b3.b 依然可以这样改进哦,博主很懒代码还没写过,脑补一下觉得写起来应该不难的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大困困瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值