Doug's MATLAB Video Tutorials

December 16th, 2008

Puzzler: Intermediate sums

MATLAB challenges get thrown around the halls of The MathWorks with fair regularity. I thought this most recent one would make a good puzzler.

You have a matrix that has 3xN rows and you want to expand it such that after every third row of the original, you insert a new row which is the sum of those three rows.

Example:


a = ...
[1 2 3
1 2 3
1 2 3
3 2 1
2 1 3
1 3 2]

Yields:

[1 2 3
1 2 3
1 2 3
3 6 9
3 2 1
2 1 3
1 3 2
6 6 6]

How would you do this?

For the more advanced MATLAB users, you might want to consider algorithms that use preallocation so that on larger matrices you are more memory efficient. Everyone should also think about readability of the code. I always think that a future version of me is going to look at this code, I better make it easy to understand in a week.

Please put your answers into the comments section


As always with these puzzlers, be sure to use the proper tags in the comments.


<pre> <code>
[deltaX, deltaY] = puzzler(inMatrix)
%% All the code so someone can just copy and paste it from the comments.
</code> </pre>

6 Responses to “Puzzler: Intermediate sums”

  1. Jos replied on :

    One solution could be:

     
    % reshape the 3xN-by-M array into a 3-by-N*M array
    b = reshape(a,3,[]) ;
    % concatenate the sum over each 3 elements
    b = [b ; sum(b)] ;
    % reshape into a (4xN)-by-M array
    b = reshape(b,[],size(a,2)) ;
     

    or in one line

     
    % add proper comments ...
    b = reshape([reshape(a,3,[]) ; sum(reshape(a,3,[]))],[],3)
     

    Jos

  2. Darren R replied on :

    This is how I would do it. There may be bugs because I wasn’t able to test it.

     
    b = puzzler(a)
    
    [Nrows,Ncols] = size(a);
    newrows = floor(Nrows/3);
    b = zeros(Nrows+newrows,Ncols);
    
    for n=1:newrows
        b(4*n-3:4*n-1,:) = a(3*n-2:3*n,:);
        b(4*n,:) = sum(a(3*n-2:3*n,:));
    end
    
    if (Nrows/3 != newrows)
        b(4*n+1:Nrows+newrows,:) = a(3*newrows+1:Nrows,:);
    end
    
     
  3. Daniel Armyr replied on :

    Funny, I was solving a very similar problem just yesterday. I also went straight for the version jos did, although I have a preference for doing one step per line instead of long one-liners. I also did some extra reshaping in order to maintain controll.

     
    %% Reshape challenge
    a = [1 2 3; 1 2 3; 1 2 3; 3 2 1; 2 1 3; 1 3 2];
    check = [1 2 3; 1 2 3; 1 2 3; 3 6 9; 3 2 1; 2 1 3; 1 3 2; 6 6 6];
    b = reshape( a', 3, 3, [] ); % Turn into a multidimensional matrix for easy handling
    c = sum(b, 2); %Calculate sum of each segment.
    d = cat( 2, b, c ); %Stick them together.
    e = reshape ( d, 3, [] )'; %Revert to original shape.
    % Check that the answer is correct.
    verify = ( e == check );
    disp ( all(verify(:)) )
     

    I would like to take this oppertunity to bring up a large reason for confusion when I work with Matlab. When reshaping matrices, I find it very difficult to remember in what way I need to transpose the data first and then in what order I need to set my size aguments and finally how I have to permute the result to get the data in the format I want. The fact that the ordering of coordinates (row/column or column/row) is not perfectly consistent doesn’t really help. Maybe this is a fundamental difficulty of matrix problems, but I feel that it should be possible to either make a tool that helps or write up a simple non-nonsens cheat-sheet for these kinds of problems.

    Just my €0.02.

    –DA

  4. Daniel Armyr replied on :

    Oh yeah, kudos to Darren R who sees to have taken into account input that does not have a length which is an even multiple of 3.

  5. dhull replied on :

    @Daniel,

    I like your solution. Let me tell you why.

    You use a test case that is built right into the code. You could comment it out later so that is available not only for testing, but also for documenting the function.

    You do one distinct operation per line. When it comes time to debug, you would be able to watch each step to understand what is going on.

    You use the “readable functions” (cat instead of [b; c], you could read your code out loud and understand it better)

    You explicitly call out the direction of summing and concatenation so there is no doubt what is going on.

    While some of your variable names are not implicitly descriptive, they do show an orderly progression of intermediate results.

    Variable names that do deserve descriptive names have them.

    This is solid code that anyone, including you in a week, will be able to understand and maintain. I think this is very important.

    Thank you,
    Doug

  6. Daniel replied on :
     
    clear all;
    clc;
    
    a =...
    [1 2 3
    1 2 3
    1 2 3
    3 2 1
    2 1 3
    1 3 2]
    
    k=3 % sum over k rows
    
    [l,c]=size(a);
    
    ind=cumsum(repmat([1 k-1],1,l/k)) % contains line indices
                                      % to sum up over
                                      % [form_1 to_1 from_2 to_2 ...]
    
    mysum=@(n) sum(a(ind(n):ind(n+1),:)); % take indices ind and sum from
                                          % one to the next in the list
    
    j=1:2:2*l/k; % take one odd ones
    
    b=zeros(l+numel(j),c); % preallocate b
    ind2=~mod(1:size(b,1),k+1)'; % where to put the sum lines
    b(~ind2,:)=a; % where to put the rest
    b(ind2,:)=cell2mat(arrayfun(mysum,j,'UniformOutput',false)') % build the sums
     

    I didn’t even think about the reshape command… :-(

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


MathWorks

Doug Hull is a proud MathWorker who is on a mission to help you with MATLAB.

Doug's picture

These postings are the author's and don't necessarily represent the opinions of The MathWorks.