Vectorizing sums of different diagonals in a matrix

后端 未结 4 1832
慢半拍i
慢半拍i 2021-01-17 17:46

I want to vectorize the following MATLAB code. I think it must be simple but I\'m finding it confusing nevertheless.

r = some constant less than m or n
[m,n         


        
相关标签:
4条回答
  • 2021-01-17 18:02

    If I understand correctly, you're trying to calculate the diagonal sum of every subarray of C, where you have removed the last row and column of C (if you should not remove the row/col, you need to loop to m-r+1, and you need to pass the entire array C to the function in my solution below).

    You can do this operation via a convolution, like so:

    S = conv2(C(1:end-1,1:end-1),eye(r),'valid');
    

    If C and r are large, you may want to have a look at CONVNFFT from the Matlab File Exchange to speed up calculations.

    0 讨论(0)
  • 2021-01-17 18:13

    Based on the idea of JS, and as Jonas pointed out in the comments, this can be done in two lines using IM2COL with some array manipulation:

    B = im2col(C, [r r], 'sliding');
    S = reshape( sum(B(1:r+1:end,:)), size(C)-r+1 );
    

    Basically B contains the elements of all sliding blocks of size r-by-r over the matrix C. Then we take the elements on the diagonal of each of these blocks B(1:r+1:end,:), compute their sum, and reshape the result to the expected size.


    Comparing this to the convolution-based solution by Jonas, this does not perform any matrix multiplication, only indexing...

    0 讨论(0)
  • 2021-01-17 18:18

    Is this what you're looking for? This function adds the diagonals and puts them into a vector similar to how the function 'sum' adds up all of the columns in a matrix and puts them into a vector.

    function [diagSum] = diagSumCalc(squareMatrix, LLUR0_ULLR1)
    % 
    % Input: squareMatrix: A square matrix.
    %        LLUR0_ULLR1:  LowerLeft to UpperRight addition = 0     
    %                      UpperLeft to LowerRight addition = 1
    % 
    % Output: diagSum: A vector of the sum of the diagnols of the matrix.
    % 
    % Example: 
    % 
    % >> squareMatrix = [1 2 3; 
    %                    4 5 6;
    %                    7 8 9];
    % 
    % >> diagSum = diagSumCalc(squareMatrix, 0);
    % 
    % diagSum = 
    % 
    %       1 6 15 14 9
    % 
    % >> diagSum = diagSumCalc(squareMatrix, 1);
    % 
    % diagSum = 
    % 
    %       7 12 15 8 3
    % 
    % Written by M. Phillips
    % Oct. 16th, 2013
    % MIT Open Source Copywrite
    % Contact mphillips@hmc.edu fmi.
    % 
    
    if (nargin < 2)
        disp('Error on input. Needs two inputs.');
        return;
    end
    
    if (LLUR0_ULLR1 ~= 0 && LLUR0_ULLR1~= 1)
        disp('Error on input. Only accepts 0 or 1 as input for second condition.');
        return;
    end
    
    [M, N] = size(squareMatrix);
    
    if (M ~= N)
        disp('Error on input. Only accepts a square matrix as input.');
        return;
    end
    
    diagSum = zeros(1, M+N-1);
    
    if LLUR0_ULLR1 == 1
        squareMatrix = rot90(squareMatrix, -1);
    end
    
    for i = 1:length(diagSum)
        if i <= M
            countUp = 1;
            countDown = i;
            while countDown ~= 0
                diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
                countUp = countUp+1;
                countDown = countDown-1;
            end
        end
        if i > M
            countUp = i-M+1;
            countDown = M;
            while countUp ~= M+1
                diagSum(i) = squareMatrix(countUp, countDown) + diagSum(i);
                countUp = countUp+1;
                countDown = countDown-1;
            end
        end
    end
    

    Cheers

    0 讨论(0)
  • 2021-01-17 18:20

    I would think you might need to rearrange C into a 3D matrix before summing it along one of the dimensions. I'll post with an answer shortly.

    EDIT

    I didn't manage to find a way to vectorise it cleanly, but I did find the function accumarray, which might be of some help. I'll look at it in more detail when I am home.

    EDIT#2

    Found a simpler solution by using linear indexing, but this could be memory-intensive.

    At C(1,1), the indexes we want to sum are 1+[0, m+1, 2*m+2, 3*m+3, 4*m+4, ... ], or (0:r-1)+(0:m:(r-1)*m)

    sum_ind = (0:r-1)+(0:m:(r-1)*m);
    

    create S_offset, an (m-r) by (n-r) by r matrix, such that S_offset(:,:,1) = 0, S_offset(:,:,2) = m+1, S_offset(:,:,3) = 2*m+2, and so on.

    S_offset = permute(repmat( sum_ind, [m-r, 1, n-r] ), [1, 3, 2]);
    

    create S_base, a matrix of base array addresses from which the offset will be calculated.

    S_base = reshape(1:m*n,[m n]);
    S_base = repmat(S_base(1:m-r,1:n-r), [1, 1, r]);
    

    Finally, use S_base+S_offset to address the values of C.

    S = sum(C(S_base+S_offset), 3);
    

    You can, of course, use bsxfun and other methods to make it more efficient; here I chose to lay it out for clarity. I have yet to benchmark this to see how it compares with the double-loop method though; I need to head home for dinner first!

    0 讨论(0)
提交回复
热议问题