计算数组中下n个元素的乘积

时间:2016-09-04 14:21:29

标签: arrays matlab matrix multiplication sliding-window

我想计算矩阵的下一个n相邻元素的乘积。要乘以的元素的数量n应在函数的输入中给出。 例如,对于这个输入,我应该从第一个开始计算每3个连续元素的乘积。

[p, ind] = max_product([1 2 2 1 3 1],3);

这会给[1*2*2, 2*2*1, 2*1*3, 1*3*1] = [4,4,6,3]

有没有实用的方法呢?现在我使用:

for ii = 1:(length(v)-2)
    p = prod(v(ii:ii+n-1));
end

其中v是输入向量,n是要乘的元素数。

在此示例n=3中,但可以采用任何正整数值。

根据n是奇数还是偶数或length(v)是奇数还是偶数,我有时得到正确答案,但有时会出错。
例如参数:

v = [1.35912281237829 -0.958120385352704 -0.553335935098461 1.44601450110386 1.43760259196739 0.0266423803393867 0.417039432979809 1.14033971399183 -0.418125096873537 -1.99362640306847 -0.589833539347417 -0.218969651537063 1.49863539349242 0.338844452879616 1.34169199365703 0.181185490389383 0.102817336496793 0.104835620599133 -2.70026800170358 1.46129128974515 0.64413523430416 0.921962619821458 0.568712984110933] 
n = 7

我收到错误:

Index exceeds matrix dimensions.
Error in max_product (line 6)  
p = prod(v(ii:ii+n-1));

有没有正确的一般方法呢?

6 个答案:

答案 0 :(得分:7)

根据Fast numpy rolling_product中的解决方案,我想建议一个MATLAB版本,它利用 R2016a 中引入的movsum函数。

数学推理是数字的乘积等于其对数之和的指数:

enter image description here

上述可能的MATLAB实现可能如下所示:

function P = movprod(vec,window_sz)
  P = exp(movsum(log(vec),[0 window_sz-1],'Endpoints','discard'));
  if isreal(vec)   % Ensures correct outputs when the input contains negative and/or
    P = real(P);   %   complex entries.
  end
end

几点说明:

  1. 我没有对此解决方案进行基准测试,也不知道它在性能方面与其他建议的比较。
  2. 它应该适用于包含零和/或负面和/或复杂元素的矢量。
  3. 可以轻松扩展以接受维度(对于数组输入)以及movsum提供的任何其他自定义。
  4. 假设1 st 输入为doublecomplex double 向量。
  5. 输出可能需要四舍五入。

答案 1 :(得分:5)

更新

受到精心设计的answer of Dev-iL的启发,这个方便的解决方案不需要 Matlab R2016a或更高版本

out = real( exp(conv(log(a),ones(1,n),'valid')) )

基本思想是将乘法转换为和,并且可以使用移动平均值,而这可以通过conv解决方案来实现。

旧答案

这是使用gallery获取循环矩阵并在乘以元素之前索引结果矩阵的相关部分的一种方法:

a = [1 2 2 1 3 1]
n = 3

%// circulant matrix
tmp = gallery('circul', a(:))
%// product of relevant parts of matrix
out = prod(tmp(end-n+1:-1:1, end-n+1:end), 2)
out =

     4
     4
     6
     3

如果输入中没有零,内存效率更高的替代

a = [10 9 8 7 6 5 4 3 2 1]
n = 2

%// cumulative product
x = [1 cumprod(a)] 
%// shifted by n and divided by itself
y = circshift( x,[0 -n] )./x 
%// remove last elements 
out = y(1:end-n) 
out =

    90    72    56    42    30    20    12     6     2

答案 2 :(得分:2)

你的方法是正确的。您应该将for循环更改为for ii = 1:(length(v)-n+1),然后它才能正常工作。

如果您不打算处理大量输入,则另一种方法是使用gallery @thewaywewalk's answer中所述的FileStream stream=File.OpenRead(@"D:\FileDownLoad\DeskTop\1.txt"); MemoryStream MS=new MemoryStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(MS); zipOutputStream.SetLevel(9); ZipEntry entry = new ZipEntry("1.txt"); zipOutputStream.PutNextEntry(entry); byte[] buffer = new byte[stream.Length]; int byteRead = 0; while ((byteRead = stream.Read(buffer, 0, buffer.Length)) > 0) zipOutputStream.Write(buffer, 0, byteRead); zipOutputStream.IsStreamOwner = false; stream.Close(); zipOutputStream.Close(); MS.Position = 0; Response.ContentType = "application/application/octet-stream"; Response.AppendHeader("content-disposition", "attachment; filename=\"Download.zip\""); Response.BinaryWrite(MS.ToArray());

答案 3 :(得分:2)

我认为问题可能基于您的索引。声明for ii = 1:(length(v)-2)的行未提供ii的正确范围。

试试这个:

function out = max_product(in,size)
    size = size-1;                 % this is because we add size to i later
    out = zeros(length(in),1)      % assuming that this is a column vector
    for i = 1:length(in)-size
        out(i) = prod(in(i:i+size));
    end

您的代码在重新调整时可以正常工作:

for ii = 1:(length(v)-(n-1))
    p = prod(v(ii:ii+(n-1)));
end

这应该照顾索引问题。

答案 4 :(得分:1)

使用bsxfun创建矩阵,每行包含连续的3个元素,然后采用矩阵的第二维。我认为这是最有效的方式:

max_product = @(v, n) prod(v(bsxfun(@plus, (1 : n), (0 : numel(v)-n)')), 2);
p = max_product([1 2 2 1 3 1],3)

更新: 其他一些解决方案已经更新,有些像@ Dev-iL的答案优于其他解决方案,我可以建议fftconv在Octave中表现优于conv

答案 5 :(得分:1)

如果您可以升级到R2017a,则可以使用新的movprod功能计算窗口产品。