Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

Find Largest Positive Value Next to Zero

Recently, Steve wrote a blog discussing code clarity/obscurity in the context of one-line code solutions. Simply stated, the problem he solved is this. Find the largest value in an array adjacent to a zero value. Must be zillions of ways, right?

Contents

An Example

Let me start with an example. If the array in question is A,

A =  [1, 5, 3, 0, 2, 7, 0, 8, 9, 1 0];

the correct answer is 8.

Steve's First Solution

Steve, being an image processing guru, immediately arrived at a solution using image processing techniques. For me, not so obvious what is going on! If I inherited this code, I'd have some work cut out for me to understand it.

fs = @(A) max(A(imdilate(A == 0, [1 1 1])));
fs(A)
ans =
     8

Good for Only Positive Values

Steve then noticed his solution was only good for positive values.

B = [5 4 -1 0 -2 0 -5 8];
fs(B)
ans =
     0

Steve's Second Answer

So Steve tried again. Again, though I understand what a mask is, I don't fully comprehend the solution, and would probably resort to reading the imdilate documentation from Image Processing Toolbox.

zero_mask = (B == 0);
adjacent_to_zero_mask = imdilate(zero_mask, [1 0 1]);
max_value_adjacent_to_zero = max(B(adjacent_to_zero_mask))
max_value_adjacent_to_zero =
    -1

Doug's answer

Here we see Doug's answer, written without dependence on anything but MATLAB. Very nice solution, but I have to think about it a bit. Again, I know what a mask is, I see we are looking for NaN values after division by 0, so we'll find the zeros, convolve to get the neighbors of the zeros, and find the maximum of those. Whew! I'm working hard now but I get it.

mask = isnan(conv(1./B,[0 1 0],'same'));
fd = @(B) max(B(isnan(conv(1./B,[0 1 0],'same'))));
fd(B)
ans =
    -1

Jos' Solution Next

The next suggestion that appeared is from Jos. It's dense, but ultimately comprehensible to me. First, create a 2 row vector with the data padded at each end with the value 1, and aligned so the first value is above the 3rd, the 2nd above the 4th, etc. (i.e., shifted over by 2 elements). Check each column to see if any of the values are 0. Select the data from the columns with a zero value, and find the maximum of those values.

fj = @(X) max(X(any([1 X(1:end-1) ; X(2:end) 1]==0)))
fj(A)
fj(B)
fj = 
    @(X)max(X(any([1,X(1:end-1);X(2:end),1]==0)))
ans =
     8
ans =
    -1

Start Over

My turn to start over and think about the problem. First, I want to find the indices of zeros in the array. From there, I construct an array of the neighbors.

zind = find(B==0)
maxcandidates = [zind-1 zind+1]
zind =
     4     6
maxcandidates =
     3     5     5     7

Next, I weed out values outside the range (possibly the first and last).

maxcandidates = maxcandidates((maxcandidates > 0) & (maxcandidates <= length(B)))
maxcandidates =
     3     5     5     7

Now I index into my candidates and get the maximum value!

max(B(maxcandidates))
ans =
    -1

As Chef Tell would say, "Very simple, very easy."

I could write this algorithm in one statement, but I fear it would immediately lose clarity.

Repeated Zeros - Clarity of Problem Statement

I got to wondering what result the originator of the question had in mind if there were repeated zeros. Why? Because I could possibly get a zero result? Was that intended? All the solutions here allow that, so mine does too and I don't need to do anything more. Let's check what happens.

C = [1 2 -4 0 0];
fj(C)
ans =
     0

Your Thoughts?

It can definitely be fun and challenging to write nifty, compact, and very dense one-liners in MATLAB. But the horror of going back to that code later, deciphering it so it can be updated or whatever, is not for me typically. I like to write code where the comments don't have to be a lot longer than the code! What are your thoughts? Post them here.




Published with MATLAB® 7.8


  • print

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.