Can't Understand 1 Line of Code in C++ STL Source: Lower_Bound/Upper_Bound

311 Views Asked by At

I am writing some code to find the last key whose value is no more than a given integer for PHP.
E.g.,array(0=>1,1=>2,2=>3,3=>3,4=>4). Given integer 3, I will find the key 3.(Binary Search)

And I looked for some references about binary search on the Internet.
I find this, which is to find the first key whose value is no less than a given integer for C++.
It says:

template <class _ForwardIter, class _Tp, class _Distance>
_ForwardIter __lower_bound(_ForwardIter __first, _ForwardIter __last,
                           const _Tp& __val, _Distance*) 
{
  _Distance __len = 0;
  distance(__first, __last, __len);
  _Distance __half;
  _ForwardIter __middle;

  while (__len > 0) {
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half);
    if (*__middle < __val) {
      __first = __middle;
      ++__first;
      __len = __len - __half - 1;
    }
    else
      __len = __half;        //    <======this line
  }
  return __first;
}

Well, why using "__len = __half;" rather than "__len = __half + 1;"?
Won't the key/value ,which "_middle" refers to in each loop, be forgotten and get lost in this binary-searching-process?
I mean, it seem that the two "__len"'s won't add up to the full "__len", seems that the __middle has been skipped

PS: My PHP code for my original question is:

$cid_start  = $count - 1;
$len        = $count;
while($len > 0){
    $half   = $len >> 1;
    $middle = $cid_start - $half;
    if($c_index[$middle][1] > $time_start){
        $cid_start = $middle - 1;
        $len       = len       - $half - 1;
    }else{
        $len       = $half + 1;
    }
}

Will it work? Or will it err?
And how can I get a -1 or something as the result when I find nothing in array?

2

There are 2 best solutions below

0
On

Just to answer the "why not …?" question: This algorithm works slightly different. If the lower bound is actually the first element, we would get a problem.

__len would be halfed until it's 2:

while (__len > 0)
{
    __half = __len >> 1; // __half is __len/2 floored

    __middle = __first + __half; // Assuming random access iterators, simplified

    if (*__middle < __val) // Lower bound is __first, so *__middle >= __val
    {
      // […]
    }
    else
      __len = __half + 1; // .. self-explanatory
}

, and then we get an infinite loop. I.e., when __len == 2:

while (__len > 0) // Okay, 2 > 0
{
    __half = __len >> 1; // __half is now 2 >> 1, which is 1

    __middle = __first + __half; // Rewritten for clarity. __middle == __first.

    if (*__middle < __val) // lower bound is at __first, so *__middle >= __val
    {
      // […]
    }
    else
      __len = __half + 1; // __len is 1 + 1 == 2
} // Endless loop

This cannot happen if the assigned length is __half itself - then for __len == 2 we get 1, and for __len == 1 we get 0.

0
On

The algorithm for binary search is very simple.

/**
 * Search $value in $array
 * Return the position in $array when found, -1 when not found
 * $array has numeric consecutive keys (0..count($array)-1)
 * $array is sorted ascending; this condition is mandatory for binary search
 * if $array is not sorted => the output is rubbish
 */
function search($value, array $array)
{
    // At each step search between positions $start and $end (including both)
    $start = 0;
    $end   = count($array) - 1;

    // End when the search interval shrunk to nothing
    while ($start <= $end) {
        // Get the middle of the interval
        // This is shorter and faster than intval(($start + $end) / 2)
        $middle = ($start + $end) >> 1;

        // Check the value in the middle of the current search interval
        if ($value == $array[$middle]) {
            // Found
            return $middle;
        }

        // Not found yet; the binary step: choose a direction
        if ($value < $array[$middle]) {
            // Search in the left half
            $end = $middle - 1;
        } else {
            // Search in the right half
            $start = $middle + 1;
        }
    }

    // Not found
    return -1;
}