Editing Binary search

Jump to: navigation, search

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision Your text
Line 55: Line 55:
 
     {
 
     {
 
         int m = (l+r)/2;
 
         int m = (l+r)/2;
         if (A[m] <= c)      /* Note use of <= instead of <. */
+
         if (A[m] <= c)      /* Note use of < instead of <=. */
 
             l = m+1;
 
             l = m+1;
 
         else
 
         else
Line 71: Line 71:
 
# The index we are seeking is at most <code>r</code>.
 
# The index we are seeking is at most <code>r</code>.
 
This is certainly true immediately before the first iteration of the loop, where <code>l = 0</code> and <code>r = n</code>. Now we claim that if it is true before a given iteration of the loop, it will be true after that iteration, which, by induction, implies our claim. First, the value <code>m</code> will be exactly the arithmetic mean of <code>l</code> and <code>r</code>, if their sum is even, or it will be 0.5 less than the mean, if their sum is odd, since <code>m</code> is computed as the arithmetic mean rounded down. This implies that, in any given iteration of the loop, <code>m</code>'s initial value will be at least <code>l</code>, and strictly less than <code>r</code>. Now:
 
This is certainly true immediately before the first iteration of the loop, where <code>l = 0</code> and <code>r = n</code>. Now we claim that if it is true before a given iteration of the loop, it will be true after that iteration, which, by induction, implies our claim. First, the value <code>m</code> will be exactly the arithmetic mean of <code>l</code> and <code>r</code>, if their sum is even, or it will be 0.5 less than the mean, if their sum is odd, since <code>m</code> is computed as the arithmetic mean rounded down. This implies that, in any given iteration of the loop, <code>m</code>'s initial value will be at least <code>l</code>, and strictly less than <code>r</code>. Now:
* ''Case 1'': <code>A[m] &lt; c</code>. This means that no element with index between <code>l</code> and <code>m</code>, inclusive, can be greater than <code>c</code>, because the array is sorted. But because we know that such an element exists (or that it is the hypothetical element just past the end of the array) in the current range delineated by indices <code>l</code> and <code>r</code>, we conclude that it must be in the second half of the range with indices from <code>m+1</code> to <code>r</code>, inclusive. In this case we set <code>l = m+1</code> and at the end of the loop the invariant is again satisfied. This is guaranteed to increase the value of <code>l</code>, since <code>m</code> starts out with value at least <code>l</code>. It also does not increase <code>l</code> beyond <code>r</code>, since <code>m</code> is initially strictly less than <code>r</code>.
+
* ''Case 1'': <code>A[m] &lt; c</code>. This means that no element with index between <code>l</code> and <code>m</code>, inclusive, can be greater than <code>c</code>, because the array is sorted. But because we know that such an element exists (or that it is the hypothetical element just past the end of the array) in the current range delineated by indices <code>l</code> and <code>r</code>, we conclude that it must be in the second half of the range with indices from <code>m+1</code> to <code>r</code>, inclusive. In this case we set <code>l = m+1</code> and at the end of the loop the invariant is again satisfied. This is guaranteed to increase the value of <code>l</code>, since <code>m</code> starts out with value at least <code>l</code>. It also does not increase <code>l</code> beyond <code>r</code>, since <code>m</code> is initially strictly less than <code>m</code>.
 
* ''Case 2'': <code>A[m] &gt;= c</code>. This means that there exists an element with index between <code>l</code> and <code>m</code>, inclusive, which is at least <code>c</code>. This implies that the first such element is somewhere in this half of the range, because the array is sorted. Therefore, after setting <code>r = m</code>, the range of indices between <code>l</code> and <code>r</code>, inclusive, will still contain the index we are seeking, and hence the invariant is again satisfied. This is guaranteed to decrease the value of <code>r</code>, since <code>m</code> starts out strictly less than <code>r</code>; it also will not decrease it beyond <code>l</code>, since <code>m</code> starts out at least as large as <code>l</code>.
 
* ''Case 2'': <code>A[m] &gt;= c</code>. This means that there exists an element with index between <code>l</code> and <code>m</code>, inclusive, which is at least <code>c</code>. This implies that the first such element is somewhere in this half of the range, because the array is sorted. Therefore, after setting <code>r = m</code>, the range of indices between <code>l</code> and <code>r</code>, inclusive, will still contain the index we are seeking, and hence the invariant is again satisfied. This is guaranteed to decrease the value of <code>r</code>, since <code>m</code> starts out strictly less than <code>r</code>; it also will not decrease it beyond <code>l</code>, since <code>m</code> starts out at least as large as <code>l</code>.
 
Since each iteration of the loop either increases <code>l</code> or decreases <code>r</code>, the loop will eventually terminate with <code>l = r</code>. Since the invariant still holds, this unique common value must be the solution. <math>_{\blacksquare}</math>
 
Since each iteration of the loop either increases <code>l</code> or decreases <code>r</code>, the loop will eventually terminate with <code>l = r</code>. Since the invariant still holds, this unique common value must be the solution. <math>_{\blacksquare}</math>
Line 108: Line 108:
  
 
More generally, binary search is often very useful for problems that read something like "find the maximum <math>x</math> such that <math>P(x)</math>". For example, in {{Problem|ccc03s5|Trucking Troubles}}, the problem essentially down to finding the maximum <math>x</math> such that the edges of the given graph of weight at least <math>x</math> span the graph. This problem can be solved using [[Prim's algorithm]] to construct the "maximum spanning tree" (really, the minimum spanning tree using negated edge weights), but binary search provides a simpler approach. We know that <math>x</math> is somewhere in between the smallest edge weight in the graph and the largest edge weight in the graph, so we start by guessing a value roughly halfway in-between. Then we use [[depth-first search]] to test whether the graph is connected using only the edges of weight greater than or equal to our guess value. If it is, then the true value of <math>x</math> is greater than or equal to our guess; if not, then it is less. This is then repeated as many times as necessary to determine the correct value of <math>x</math>.
 
More generally, binary search is often very useful for problems that read something like "find the maximum <math>x</math> such that <math>P(x)</math>". For example, in {{Problem|ccc03s5|Trucking Troubles}}, the problem essentially down to finding the maximum <math>x</math> such that the edges of the given graph of weight at least <math>x</math> span the graph. This problem can be solved using [[Prim's algorithm]] to construct the "maximum spanning tree" (really, the minimum spanning tree using negated edge weights), but binary search provides a simpler approach. We know that <math>x</math> is somewhere in between the smallest edge weight in the graph and the largest edge weight in the graph, so we start by guessing a value roughly halfway in-between. Then we use [[depth-first search]] to test whether the graph is connected using only the edges of weight greater than or equal to our guess value. If it is, then the true value of <math>x</math> is greater than or equal to our guess; if not, then it is less. This is then repeated as many times as necessary to determine the correct value of <math>x</math>.
 
Binary search can also be used to find approximate solutions to equations. For example, suppose we have <math>c</math> blocks, and we wish to use as many of them as possible to construct a square-based pyramid. A square-based pyramid has an apex consisting of a single block. The next layer under it is a 2 block by 2 block square (so 4 blocks in all), then there is a layer with 9 blocks, and so on down. How many layers can we construct using the blocks given?
 
 
It can be shown that there a square-based pyramid with <math>n</math> levels requires <math>f(n) = \frac{1}{6}(2n^3 + 3n^2 + n)</math> blocks. This polynomial is increasing on <math>[0, \infty)</math>. But of course as a trivial upper bound we cannot construct more that <math>c</math> levels using <math>c</math> blocks. So, by using binary search, we can find the maximum <math>x \in [0, c)</math> such that <math>\frac{1}{6}(2x^3 + 3x^2 + x) \leq c</math>. (More efficient solutions are accessible with higher mathematics.)
 
  
 
==Continuous==
 
==Continuous==
The continuous version of the problem is like the number guessing game applied to real numbers: Alice thinks of a real number between 0 and 1, and Bob has to guess it. Bob begins by guessing 0.5, and so on. Clearly, in this case, no matter how many questions Bob asks, there will still be an infinite number of possibilities, though he can get greater and greater accuracy by asking more and more questions. (For example, after 10 questions, he might have narrowed down the range to (0.1015625, 0.1025390625)). However, in computer science, we are often satisfied if we can approximate the correct answer to arbitrary precision, rather than determine it exactly outright. (After all, the native real types of most microprocessors (see [[IEEE 754]]) can only store a fixed number of bits of precision, anyway.) Here, if, for example, Bob wants to determine Alice's number to an accuracy of one part in 10<sup>9</sup>, then he has to ask about 30 questions, because the length of the original interval is 1, and this must be reduced by a factor of 10<sup>9</sup>, which is about 2<sup>30</sup>. In general, the time taken will be approximately <math>\log_2\frac{\ell}{\epsilon_a}</math>, where <math>\ell</math> is the length of the original interval and <math>\epsilon_a</math> is the desired precision (expressed as absolute error).
+
TODO
 
+
More formally, we have a monotonically increasing function <math>f:A \to B</math> where <math>A</math> is usually an interval of the real line and <math>B</math> is some totally ordered set, and we know that there is some <math>x_0 \in A</math> such that <math>f(x_0) = c \in B</math>. (Such a guarantee can be provided by the intermediate value theorem, if <math>f</math> is a continuous function.) Our objective is to find <math>x</math> such that <math>|x - x_0| < \epsilon</math>, that is, to approximate <math>f^{-1}(c)</math> to some desired precision.
+
 
+
This is a very general method for computing inverses of monotone functions. More specific (and faster) methods exist for specific classes of functions (such as Newton's method, the Lagrange inversion formula, or integral representations that can be computed using Gaussian quadrature); however, binary search is expedient and efficient when the precision required is not too large, and can be used for nearly all functions, because chances are they will be monotone on ''some'' interval around <math>f^{-1}(c)</math>. The exceptions occur when the function oscillates wildly around that point (such functions are usually "pathological", and occur only in discussion of mathematical theory and not in practice), or the function has a local extremum at <math>c</math> (here, [[ternary search]] may be used instead).
+
 
+
An example problem of a different type is {{Problem|secret|Secret Room}}. This can be solved by repeatedly guessing some radius <math>r</math> and trying to figure out whether a circle of radius <math>r</math> can be placed somewhere on the grid without crossing any lasers; if so, then the maximum radius is at least <math>r</math>, and otherwise, it is less. (However, determining whether placing the circle is possible is a nontrivial problem in [[computational geometry]].)
+
  
 
==Problems==
 
==Problems==
Line 128: Line 118:
 
* {{Problem|ccc08s2p6|Landing}}
 
* {{Problem|ccc08s2p6|Landing}}
 
* {{Problem|ccc96s5|Max Distance}}
 
* {{Problem|ccc96s5|Max Distance}}
* {{Problem|secret|Secret Room}}
 
 
* {{Problem|ccc03s5|Trucking Troubles}}
 
* {{Problem|ccc03s5|Trucking Troubles}}
 
* {{SPOJ|AGGRCOW|Aggressive Cows @ SPOJ}}
 
* {{SPOJ|AGGRCOW|Aggressive Cows @ SPOJ}}
Line 136: Line 125:
 
* {{SPOJ|GLASNICI|Glasnici @ SPOJ}}
 
* {{SPOJ|GLASNICI|Glasnici @ SPOJ}}
 
* {{SPOJ|PIE|Pie @ SPOJ}}
 
* {{SPOJ|PIE|Pie @ SPOJ}}
 +
 +
[[Category:Incomplete]]

Please note that all contributions to PEGWiki are considered to be released under the Attribution 3.0 Unported (see PEGWiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

Cancel | Editing help (opens in new window)