【Bluestein's Algorithm】[POJ2821]TN's Kingdom III - Assassination

题目大意

TN要暗杀Dzx,为了保密,他想到了这样一种方式:首先,把信息编码为N个实数,组成序列α,之后再随便搞一个长度为N的实数序列β。然后按照下面的步骤计算序列γ:
0、做一个空序列γ。
1、把β倒过来。
2、把β向右平移一个元素。最右侧的元素补到左边。
3、计算此时α和β对应元素的积的和。将其加到γ的末尾。
4、如果γ还不足N个元素,重复步骤2和3。

虽然这种加密方法是很弱的,可是那些笨蛋刺客们却没法破解。然后,这些东西就被Dzx拿到了,于是他就躲过了暗杀……现在作为历史学家的你得到了β和γ,你需要解出α来获得研究TN的材料。

分析

说得更直白一点, γ γ 就是 α α β β 的循环卷积。

运用 FFT F F T 所进行的卷积本身就是循环卷积

我们假设 c c a b b 两个序列的卷积,即ck=i=0kak×bki
令n为大于等于c的长度而且是2的整数次幂的数,那么卷积也可以写成这个形式 ck=i,jakbj[i+j0(modn)] c k = ∑ i , j a k b j [ i + j ≡ 0 ( mod n ) ]
FFT就是根据第二个式子算出来的,当 a,b a , b 长度相等而且 n n 等于他们的长度的时候,根据这个式子算出来的就是循环卷积。平时计算的时候由于nc的长度,所以和线性卷积的式子没有区别。
很显然,我们对 γ γ β β DFT D F T ,然后相除,再 IDFT I D F T 即可,但是用 FFT F F T 进行 DFT D F T 要求长度必须是2的整数次幂,如果原长不是2的整数次幂,我们强行弄成2的整数次幂的话,在做 γ γ DFT D F T 和最后 IDFT I D F T 时就会出现问题,因为你根本不知道你这样做完了是一个什么东西。
所以,我们需要一个可以做任意长度卷积的东西, Bluesteins  Algorithm B l u e s t e i n ′ s     A l g o r i t h m 或者混合基 FFT F F T

Bluestein’s Algorithm

我们考虑 DFT D F T 的式子

AkjkAk=j=0N1ajωjkN=j=0N1aje2πiNjk=(jk)2+j2+k22=j=0N1ajeπiN((jk)2+j2+k2)=eπiNk2ajeπiNj2eπiN((kj)2)(11)(12)(13)(14)(15) (11) A k = ∑ j = 0 N − 1 a j ω N j k (12) = ∑ j = 0 N − 1 a j e 2 π i N j k (13) j k = − ( j − k ) 2 + j 2 + k 2 2 (14) A k = ∑ j = 0 N − 1 a j e π i N ( − ( j − k ) 2 + j 2 + k 2 ) (15) = e π i N k 2 ∑ a j e π i N j 2 e π i N ( − ( k − j ) 2 )

Xj=ajeπiNj2,Yj=eπiNj2 X j = a j e π i N j 2 , Y j = e − π i N j 2

Ak=eπiNk2j=0NXjYkj A k = e π i N k 2 ∑ j = 0 N X j Y k − j

这是一个卷积的形式,我们发现我们可以通过一次卷积来做一次 DFT D F T .
ps:其实是CZT,fft是dft的快速算法,其实就是N点dft算法,就是计算量小一点。N点dft的本质是z变换后,在z域单位圆上等间距N点连续采样。
IDFT I D F T 的时候,可以类比用 FFT F F T 进行 IDFT I D F T 时做出的改变即可。
我们发现 kj k − j 可能小于0,我们只需要将 Y(x) Y ( x ) 右移 N N <script type="math/tex" id="MathJax-Element-58">N</script>位即可。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const double pi=acos(-1);
#define MAXN (1<<17)
int n;
struct cpx{
    double r,i;
    inline cpx(){
    }
    inline cpx(double r,double i=0):r(r),i(i){
    }
    inline cpx operator+(const cpx &b)const{
        return cpx(r+b.r,i+b.i);
    }
    inline cpx operator-(const cpx &b)const{
        return cpx(r-b.r,i-b.i);
    }
    inline cpx operator*(const cpx &b)const{
        return cpx(r*b.r-i*b.i,r*b.i+i*b.r);
    }
    inline cpx operator/(const cpx &b)const{
        return cpx((r*b.r+i*b.i)/(b.r*b.r+b.i*b.i),(i*b.r-r*b.i)/(b.r*b.r+b.i*b.i));
    }
    inline cpx& operator*=(const cpx &b){
        return *this=*this*b;
    }
    inline cpx &operator/=(const cpx &b){
        return *this=*this/b;
    }
}beta [MAXN+10],gamma[MAXN+10],alpha[MAXN+10],A[MAXN*4+10],B[MAXN*4+10];
void fft(cpx *a,int N,int f){
    int i,j,t,k;
    for(i=1,j=0;i<N-1;i++){
        for(t=N;j^=(t>>=1),~j&t;);
        if(i<j)
            swap(a[i],a[j]);
    }
    for(i=1;i<N;i<<=1){
        cpx wn(cos(pi/i),f*sin(pi/i));
        t=i<<1;
        for(j=0;j<N;j+=t){
            cpx w(1,0);
            for(k=0;k<i;k++,w*=wn){
                cpx x(a[j+k]),y(w*a[j+i+k]);
                a[j+k]=x+y;
                a[j+i+k]=x-y;
            }
        }
    }
    if(f==-1){
        for(i=0;i<N;i++)
            a[i]/=N;
    }
}
void bluestein(cpx *a,int n,int f){
    int N,i;
    memset(A,0,sizeof A);
    memset(B,0,sizeof B);
    for(i=0;i<n;i++)
        A[i]=cpx(cos(pi*i*i/n),f*sin(pi*i*i/n))*a[i];
    for(i=0;i<(n<<1);i++)
        B[i]=cpx(cos(pi*(i-n)*(i-n)/n),-f*sin(pi*(i-n)*(i-n)/n));
    for(N=1;N<(n<<2);N<<=1);
    fft(A,N,1);
    fft(B,N,1);
    for(i=0;i<N;i++)
        A[i]*=B[i];
    fft(A,N,-1);
    for(i=0;i<n;i++){
        a[i]=A[i+n]*cpx(cos(pi*i*i/n),f*sin(pi*i*i/n));
        if(f==-1)
            a[i]/=n;
    }
}
void read(){
    scanf("%d",&n);
    int i;
    for(i=0;i<n;i++)
        scanf("%lf",&beta[i].r);
    for(i=0;i<n;i++)
        scanf("%lf",&gamma[i].r);
}
void solve(){
    bluestein(beta,n,1);
    bluestein(gamma,n,1);
    for(int i=0;i<n;i++)
        alpha[i]=gamma[i]/beta[i];
    bluestein(alpha,n,-1);
}
void print(){
    int i;
    for(i=0;i<n;i++)
        printf("%.4f\n",alpha[i].r);
}
int main()
{
    read();
    solve();
    print();
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值