我正在寻找一种优雅的方式来计算"产品"离散卷积,而不是总和。
以下是离散卷积的公式:
在这种情况下,我们可以使用:conv(x,y)
现在我想实施这些操作
当然我可以使用一个循环,但我正在寻找一个技巧来线性化这个操作。
示例:
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
答案 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)
),来自Luis的bsxfun
解决方案,以及我的cumprod
和hankel
解决方案)使用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)
但是在这里我们有两个向量f
和g
。
所需的配方可以重新配制为:
八度音阶时间:
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
})
作为列。最后计算每列元素的乘积。