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.
- Category:
- Best Practice,
- Readability