高效的比特流卷积

时间:2015-02-06 15:26:41

标签: fft convolution

我有两个长度为N的浮点时间序列A,B。我必须计算循环卷积并找到最大值。这样做的经典和最快的方法是

C = iFFT(FFT(A)* FFT(B))

现在,让我们假设A和B都是一个只包含1和0的系列,所以原则上我们可以将它们表示为比特流。

问题:如果我能够以某种方式利用上述事实,是否有更快的方法进行卷积(并找到其最大值)?

(我已经在Walsh上做了很多考虑 - Hadamard转换和SSE指令,popcounts,但发现没有更快的方式M> 2 ** 20这是我的情况。)

谢谢, GD

1 个答案:

答案 0 :(得分:0)

大小为c的两个数组ab的1D卷积n是一个数组:

formula

这个公式可以用迭代的方式重写:

formula2

总和的非空条款仅限于nb的更改次数b:如果b是一个简单模式,则此总和可以限制为几个术语。现在可以设计一种算法来计算c

1:计算c[0](约n次操作)

2:使用公式计算0<i<n计算c[i](约nb*n次操作)

如果nb很小,此方法可能比fft快。请注意,它将为比特流信号提供精确的结果,而fft需要过采样和浮点精度以提供准确的结果。

以下是使用输入类型unsigned char实现此技巧的一段代码。

#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>

#include <fftw3.h>

typedef struct{
    unsigned int nbchange;
    unsigned int index[1000];
    int change[1000];
}pattern;

void topattern(unsigned int n, unsigned char* b,pattern* bp){
    //initialisation
    bp->nbchange=0;
    unsigned int i;
    unsigned char former=b[n-1];
    for(i=0;i<n;i++){
        if(b[i]!=former){
            bp->index[bp->nbchange]=i;
            bp->change[bp->nbchange]=((int)b[i])-former;
            bp->nbchange++;
        }
        former=b[i];
    }
}

void printpattern(pattern* bp){
    int i;
    printf("pattern :\n");
    for(i=0;i<bp->nbchange;i++){
        printf("index %d change %d\n",bp->index[i],bp->change[i]);
    } 
}

//https://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer

unsigned int NumberOfSetBits(unsigned int i)
{
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

//https://stackoverflow.com/questions/2525310/how-to-define-and-work-with-an-array-of-bits-in-c

unsigned int convol_longint(unsigned int a, unsigned int b){
    return NumberOfSetBits(a&b);
}

int main(int argc, char* argv[]) {

    unsigned int n=10000000;

    //the array a
    unsigned char* a=malloc(n*sizeof(unsigned char));
    if(a==NULL){printf("malloc failed\n");exit(1);}
    unsigned int i,j;
    for(i=0;i<n;i++){
        a[i]=rand();
    }
    memset(&a[2],5,2);
    memset(&a[10002],255,20);

    for(i=0;i<n;i++){
        //printf("a %d %d \n",i,a[i]);
    }

    //pattern b
    unsigned char* b=malloc(n*sizeof(unsigned char));
    if(b==NULL){printf("malloc failed\n");exit(1);}
    memset(b,0,n*sizeof(unsigned char));
    memset(&b[2],1,20);


    //memset(&b[120],1,10);
    //memset(&b[200],1,10);

    int* c=malloc(n*sizeof(int)); //nb bit in the array
    memset(c,0,n*sizeof(int));

    clock_t begin, end;
    double time_spent;

    begin = clock();
    /* here, do your time-consuming job */


    //computing c[0]
    for(i=0;i<n;i++){
        //c[0]+= convol_longint(a[i],b[i]);
        c[0]+= ((int)a[i])*((int)b[i]);
        //printf("c[0] %d %d\n",c[0],i);
    }
    printf("c[0] %d\n",c[0]);

    //need to store b as a pattern.
    pattern bpat;
    topattern( n,b,&bpat);
    printpattern(&bpat);

    //computing c[i] according to formula
    for(i=1;i<n;i++){
        c[i]=c[i-1];
        for(j=0;j<bpat.nbchange;j++){
            c[i]+=bpat.change[j]*((int)a[(bpat.index[j]-i+n)%n]);
        }
    }

    //finding max
    int currmax=c[0];
    unsigned int currindex=0;
    for(i=1;i<n;i++){
        if(c[i]>currmax){
            currmax=c[i];
            currindex=i;
        }
        //printf("c[i] %d %d\n",i,c[i]);
    }

    printf("c[max] is %d at index %d\n",currmax,currindex);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

    printf("computation took %lf seconds\n",time_spent);


    double* dp = malloc(sizeof (double) * n);
    fftw_complex * cp = fftw_malloc(sizeof (fftw_complex) * (n/2+1));

    begin = clock();
    fftw_plan plan = fftw_plan_dft_r2c_1d(n, dp, cp, FFTW_ESTIMATE);

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

    fftw_execute ( plan );
    printf("fftw took %lf seconds\n",time_spent);

    free(dp);
    free(cp);

    free(a);
    free(b);
    free(c);
    return 0;
}

编译:{{1​​}}

对于gcc main.c -o main -lfftw3 -lmn=10 000 000nb=2只是一个&#34;矩形1D窗口&#34;)此算法在我的计算机上运行0.65秒。使用fftw的双精度fft花费大致相同的时间。与大多数比较一样,这种比较可能是不公平的,因为:

  • b是本答​​案中提出的算法的最佳案例。
  • 基于fft的算法需要过采样。
  • 基于fft的算法可能不需要双重精确度
  • 此处公开的实现未进行优化。这只是基本代码。

此实现可以处理nb=2。此时,可以建议对n=100 000 000使用long int以避免任何溢出风险。

如果信号是比特流,则可以以各种方式优化该程序。对于按位运算,请查看this questionthis one