Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here's a simple approach: </p> <ol> <li>Start at the bottom-left corner. </li> <li>If the target is less than that value, it must be above us, so <strong>move up one</strong>.</li> <li>Otherwise we know that the target can't be in that column, so <strong>move right one</strong>.</li> <li>Goto 2.</li> </ol> <p>For an <code>NxM</code> array, this runs in <code>O(N+M)</code>. I think it would be difficult to do better. :)</p> <hr> <p><strong>Edit:</strong> Lots of good discussion. I was talking about the general case above; clearly, if <code>N</code> or <code>M</code> are small, you could use a binary search approach to do this in something approaching logarithmic time. </p> <p>Here are some details, for those who are curious:</p> <h2>History</h2> <p>This simple algorithm is called a <a href="http://www.cs.geneseo.edu/~baldwin/math-thinking/saddleback.html" rel="noreferrer">Saddleback Search</a>. It's been around for a while, and it is optimal when <code>N == M</code>. Some references:</p> <ul> <li>David Gries, <strong><a href="http://rads.stackoverflow.com/amzn/click/0387964800" rel="noreferrer">The Science of Programming</a></strong>. <em>Springer-Verlag, 1989</em>.</li> <li>Edsgar Dijkstra, <strong><a href="http://www.cs.utexas.edu/users/EWD/index09xx.html" rel="noreferrer">The Saddleback Search</a></strong>. <em>Note EWD-934, 1985</em>.</li> </ul> <p>However, when <code>N &lt; M</code>, intuition suggests that binary search should be able to do better than <code>O(N+M)</code>: For example, when <code>N == 1</code>, a pure binary search will run in logarithmic rather than linear time.</p> <h2>Worst-case bound</h2> <p>Richard Bird examined this intuition that binary search could improve the Saddleback algorithm in a 2006 paper:</p> <ul> <li>Richard S. Bird, <strong><a href="http://www.cs.ox.ac.uk/publications/publication2664-abstract.html" rel="noreferrer">Improving Saddleback Search: A Lesson in Algorithm Design</a></strong>, <em>in Mathematics of Program Construction, pp. 82--89, volume 4014, 2006</em>.</li> </ul> <p>Using a rather unusual conversational technique, Bird shows us that for <code>N &lt;= M</code>, this problem has a lower bound of <code>Ω(N * log(M/N))</code>. This bound make sense, as it gives us linear performance when <code>N == M</code> and logarithmic performance when <code>N == 1</code>.</p> <h2>Algorithms for rectangular arrays</h2> <p>One approach that uses a row-by-row binary search looks like this:</p> <ol> <li>Start with a rectangular array where <code>N &lt; M</code>. Let's say <code>N</code> is rows and <code>M</code> is columns.</li> <li>Do a binary search on the middle row for <code>value</code>. If we find it, we're done.</li> <li>Otherwise we've found an adjacent pair of numbers <code>s</code> and <code>g</code>, where <code>s &lt; value &lt; g</code>.</li> <li>The rectangle of numbers above and to the left of <code>s</code> is less than <code>value</code>, so we can eliminate it.</li> <li>The rectangle below and to the right of <code>g</code> is greater than <code>value</code>, so we can eliminate it.</li> <li>Go to step (2) for each of the two remaining rectangles.</li> </ol> <p>In terms of worst-case complexity, this algorithm does <code>log(M)</code> work to eliminate half the possible solutions, and then recursively calls itself twice on two smaller problems. We do have to repeat a smaller version of that <code>log(M)</code> work for every row, <strong>but if the number of rows is small compared to the number of columns, then being able to eliminate all of those columns in logarithmic time starts to become worthwhile</strong>.</p> <p>This gives the algorithm a complexity of <code>T(N,M) = log(M) + 2 * T(M/2, N/2)</code>, which Bird shows to be <code>O(N * log(M/N))</code>.</p> <p><a href="http://twistedoakstudios.com/blog/Post5365_searching-a-sorted-matrix-faster" rel="noreferrer">Another approach posted by Craig Gidney</a> describes an algorithm similar the approach above: it examines a row at a time using a step size of <code>M/N</code>. His analysis shows that this results in <code>O(N * log(M/N))</code> performance as well.</p> <h2>Performance Comparison</h2> <p>Big-O analysis is all well and good, but how well do these approaches work in practice? The chart below examines four algorithms for increasingly "square" arrays:</p> <p><img src="https://i.stack.imgur.com/SZwvl.png" alt="algorithm performance vs squareness"></p> <p>(The "naive" algorithm simply searches every element of the array. The "recursive" algorithm is described above. The "hybrid" algorithm is an implementation of <a href="http://twistedoakstudios.com/blog/Post5365_searching-a-sorted-matrix-faster" rel="noreferrer">Gidney's algorithm</a>. For each array size, performance was measured by timing each algorithm over fixed set of 1,000,000 randomly-generated arrays.)</p> <p>Some notable points:</p> <ul> <li>As expected, the "binary search" algorithms offer the best performance on rectangular arrays and the Saddleback algorithm works the best on square arrays.</li> <li>The Saddleback algorithm performs worse than the "naive" algorithm for 1-d arrays, presumably because it does multiple comparisons on each item.</li> <li>The performance hit that the "binary search" algorithms take on square arrays is presumably due to the overhead of running repeated binary searches.</li> </ul> <h2>Summary</h2> <p>Clever use of binary search can provide <code>O(N * log(M/N)</code> performance for both rectangular and square arrays. The <code>O(N + M)</code> "saddleback" algorithm is much simpler, but suffers from performance degradation as arrays become increasingly rectangular.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload