计算产品的有效方法"离散卷积

时间:2016-12-28 17:16:23

标签: arrays matlab multiplication convolution sliding-window

我正在寻找一种优雅的方式来计算"产品"离散卷积,而不是总和。

以下是离散卷积的公式:

enter image description here

在这种情况下,我们可以使用:conv(x,y)

现在我想实施这些操作

enter image description here

当然我可以使用一个循环,但我正在寻找一个技巧来线性化这个操作。

示例:

f = [2 4 3 9 7 1]
g = [3 2 1]

dist = length(g)-1;

for ii = 1:length(f)-dist
    x(ii) = prod(f(ii:ii+dist).*g)
end
  

x =

144    648   1134    378

3 个答案:

答案 0 :(得分:5)

cumprod解决方案:(效率很高)

>> pf = cumprod(f);
>> x = prod(g).*pf(numel(g):end)./[1 pf(1:(end-numel(g)))]

x =

         144         648        1134         378

首先使用cumprod获取f的累积积。通过将每个元素除以之前的累积乘积3个元素,我们得到每个numel(g)宽滑动窗口的乘积f。然后乘以g

元素的乘积

注意:f包含许多元素或极值(大或小)时,执行累积产品时可能会出现准确性问题或下溢/溢出问题。减轻这种情况的一种可能方法是在累积产品之前应用缩放到f,然后在之后撤消它:

c = ...set a scaling factor...
pf = cumprod(f./c);
x = prod(c.*g).*pf(numel(g):end)./[1 pf(1:(end-numel(g)))];

c的选择可能类似于mean(abs(f))max(abs(f)),因此缩放后的f会给出更有界限的结果(即值接近1)。这并没有明显改变下面的时间结果。

hankel解决方案:(效率不高但仍然有趣)

>> x = prod(g).*prod(hankel(f(1:numel(g)), f(numel(g):end)))

x =

         144         648        1134         378

hankel的调用会创建一个矩阵,其中每列都包含其中一个numel(g)宽滑动窗口的内容。将产品放在每列中,然后乘以g元素的乘积得出答案。但是,对于大型向量f和/或g,这可能涉及大量额外计算并使用大量内存。

计时结果:

我测试了6个解决方案(问题中的循环,来自rahnema的2个解决方案(conv(log)movsum(log)),来自Luisbsxfun解决方案,以及我的cumprodhankel解决方案)使用f = rand(1,1000000);g = rand(1,100);并平均超过40次迭代。这就是我得到的(运行Windows 7 x64,16 GB RAM,MATLAB R2016b):

solution    | avg. time (s)
------------+---------------
loop        |       1.10671
conv(log)   |       0.04984
movsum(log) |       0.03736
bsxfun      |       1.20472
cumprod     |       0.01469
hankel      |       1.17704

答案 1 :(得分:4)

另一种解决方案部分受Dev-iL answer启发,涉及相同的问题

exp(sum(log(g))+conv(log(f),[1 1 1],'valid'))

exp(sum(log(g))+movsum(log(f),numel(g),'Endpoints', 'discard'))

exp(sum(log(x))) = prod(x)

以来

但是在这里我们有两个向量fg

而不是一个向量

所需的配方可以重新配制为:

enter image description here

八度音阶时间:

f= rand(1,1000000);
g= rand(1,100);

disp('----------EXP(LOG)------')
tic
    exp(sum(log(g))+conv(log(f),ones(1,numel(g))));
toc

disp('----------BSXFUN------')
tic
    ind = bsxfun(@plus, 0:numel(f)-numel(g), (1:numel(g)).');
    x = prod(bsxfun(@times, f(ind), g(:)),1);
toc
disp('----------HANKEL------')
tic
    prod(g)*prod(hankel(f(1:numel(g)), f(numel(g):end)));
toc

disp('----------CUMPROD-----')
tic
    pf = cumprod(f);
    x = prod(g)*pf(numel(g):end)./[1 pf(1:(end-numel(g)))];
toc

结果:

----------EXP(LOG)------%rahnema1
Elapsed time is 0.211445 seconds.
----------BSXFUN--------%Luis Mendo
Elapsed time is 1.94182 seconds.
----------HANKEL--------%gnovice
Elapsed time is 1.46593 seconds.
----------CUMPROD-------%gnovice
Elapsed time is 0.00748992 seconds.

答案 2 :(得分:2)

这是一种避免循环的方法:

ind

其工作原理如下。 g表示索引的滑动窗口,每个列对应一个位移。例如,如果3的大小为ind,则矩阵1 2 3 4 ... 2 3 4 5 ... 3 4 5 6 ... 将为

f

这用于索引g。结果乘以(带广播)<div class="drag-bound"> <div id="obj"></div> </div> $('#obj').draggable({ snap: ".drag-bound", snapTolerance: 5 }) 作为列。最后计算每列元素的乘积。