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.
Get
the MATLAB code
Published with MATLAB® 7.8



loren, don’t take the fun out of CSSM…
we know: whilst everybody is bragging with undecipherable golfers in the NG – when back in their own kitchen, they prepare the meal in edible pieces…
as ever with a :-)
urs
Hi,
For code for: Find Largest Positive Value Next to Zero
with: Repeated Zeros
I think that next code resolve that question!
Sorry but I just learn to write in english
R.
Hi Loren,
Thank you for your post. I absolutely agree with you! Condensed code can be useful for prototyping which is essentially throw-away code. In a decent SW implementation, such compact code is counterproductive because it is usually unreadable, not always backwards compatible, prone to errors, and makes debugging frustrating and almost always results is extensive recode. I prefer to stick with established and recognizable programming constructs along with meaningful comments. I write SW that are going to be used and possibly augmented by other people. Such work should not become a nightmare!
gk.
What’s wrong about this simple code line?
Thomas-
The problem with your solution is that it gives the wrong answer, if I’m not mistaken:
>> B = [5 4 -1 0 -2 0 -5 8]; min(B(B>0)) ans = 44 isn’t next to a zero, for starters.
–Loren
Rafael-
Your solution seems fine except it doesn’t get the answer 0 when there are duplicate 0 neighbors and 0 is the largest neighbor to a 0.
–Loren
How about this:
The “intersect(find(c), …” rules out runs of zeros as well as out of bounds indexes. The max handles positive and negative values. I don’t like calling “find(c==0)” twice, but there’s probably a way around that. Could always make it two lines of code.
Mike-
I think it doesn’t give the right answer for this case.
>> C = [1 2 -4 0 0]; >> max(c(intersect(find(c),[find(c==0)+1 find(c==0)-1]))) ans = -4but the answer should be 0.
–Loren
Here’s my crack at it. I don’t think it is too abstruce.
Matt-
Your solution is certainly viable as well!
–Loren
If we do want to allow answers of zero (I assumed we did not want that), then we can use this code
Now the intersect just removes out of bounds results.
It is often better to use an obvious path for understanding. I would use a combinationd of diff(A) (find the largest, not forgetting the negative left hand side) and A==0 (adjecent to 0).
I think Mike’s answer is probably the most intuitive, but I would have answered it like Jos only using “|” instead of “any”.
An even shorter code is
Again an interesting blog! However, the title of this blog is somewhat misleading. Shouldn’t the word “positive” be removed?
- jos
Jos-
You’re right about the title!
Ben-
Thanks for another solution.
–Loren
My option would be the following:
or the identical, but slightly more compact:
pretty similar to what already has been posted though.
Hi Loren,
Late to the game–have been on vacation–but wanted to throw my solution into the mix. Cheers!
Brett
First, pad B, then use |BSXFUN|:
How about this:
max(A(logical(conv(~A+0, [1 0 1], ‘same’))))
I haven’t looked at any of the other solutions, so maybe someone’s already done this.
Loren,
Your solution is clear to follow. i.e., the code demonstrates what it is doing.
The solutions that are shown from Steve, Doug & Jos are not clear. Certainly it can be fun to study them. But, I would never use them in real code.
I really think that Djykstra was correct in saying that the code should demonstrate that it is correct.