Search code examples
arraysmatlabmatrixmultiplicationsliding-window

Compute the product of the next n elements in array


I would like to compute the product of the next n adjacent elements of a matrix. The number n of elements to be multiplied should be given in function's input. For example for this input I should compute the product of every 3 consecutive elements, starting from the first.

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

This gives [1*2*2, 2*2*1, 2*1*3, 1*3*1] = [4,4,6,3].

Is there any practical way to do it? Now I do this using:

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

where v is the input vector and n is the number of elements to be multiplied.

in this example n=3 but can take any positive integer value.

Depending whether n is odd or even or length(v) is odd or even, I get sometimes right answers but sometimes an error.
For example for arguments:

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

I get the error:

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

Is there any correct general way to do it?


Solution

  • Update

    Inspired by the nicely thought answer of Dev-iL comes this handy solution, which does not require Matlab R2016a or above:

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

    The basic idea is to transform the multiplication to a sum and a moving average can be used, which in turn can be realised by convolution.


    Old answers

    This is one way using gallery to get a circulant matrix and indexing the relevant part of the resulting matrix before multiplying the elements:

    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
    

    More memory efficient alternative in case there are no zeros in the input:

    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