我有两个长度为N的浮点时间序列A,B。我必须计算循环卷积并找到最大值。这样做的经典和最快的方法是
C = iFFT(FFT(A)* FFT(B))
现在,让我们假设A和B都是一个只包含1和0的系列,所以原则上我们可以将它们表示为比特流。
问题:如果我能够以某种方式利用上述事实,是否有更快的方法进行卷积(并找到其最大值)?
(我已经在Walsh上做了很多考虑 - Hadamard转换和SSE指令,popcounts,但发现没有更快的方式M> 2 ** 20这是我的情况。)
谢谢, GD
答案 0 :(得分:0)
大小为c
的两个数组a
和b
的1D卷积n
是一个数组:
这个公式可以用迭代的方式重写:
总和的非空条款仅限于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 -lm
和n=10 000 000
(nb=2
只是一个&#34;矩形1D窗口&#34;)此算法在我的计算机上运行0.65秒。使用fftw的双精度fft花费大致相同的时间。与大多数比较一样,这种比较可能是不公平的,因为:
b
是本答案中提出的算法的最佳案例。此实现可以处理nb=2
。此时,可以建议对n=100 000 000
使用long int以避免任何溢出风险。
如果信号是比特流,则可以以各种方式优化该程序。对于按位运算,请查看this question和this one