Are you one of the 10% of programmers who can write a binary search?

There are some programming books that I’ve read from cover to cover repeatedly; there are others that I have dipped into many times, reading a chapter or so at a time.  Jon Bentley’s 1986 classic Programming Pearls is a rare case where both of these are true, as the scuffs at the bottom of my copy’s cover attest:

(I have the First Edition [amazon.comamazon.co.uk], so that’s what I scanned for the cover image above, but it would probably make more sense to get the newer and cheaper Second Edition [amazon.comamazon.co.uk] which apparently has three additional chapters.)

I’ll review this book properly in a forthcoming article (as I did for Coders at Work, The Elements of Programming Style, Programming the Commodore 64 and The C Programming Language), but for now I want to look at just one passage from the book, and consider what it means.  One astounding passage.

Only 10% of programmers can write a binary search

Every single time I read Programming Pearls, this passage brings me up short:

Binary search solves the problem [of searching within a pre-sorted array] by keeping track of a range within the array in which T [i.e. the sought value] must be if it is anywhere in the array.  Initially, the range is the entire array.  The range is shrunk by comparing its middle element to T and discarding half the range.  The process continues until T is discovered in the array, or until the range in which it must lie is known to be empty.  In an N-element table, the search uses roughly log(2) N comparisons.

Most programmers think that with the above description in hand, writing the code is easy; they’re wrong.  The only way you’ll believe this is by putting down this column right now and writing the code yourself.  Try it.

I’ve assigned this problem in courses at Bell Labs and IBM.  Professional programmers had a couple of hours to convert the above description into a program in the language of their choice; a high-level pseudocode was fine.  At the end of the specified time, almost all the programmers reported that they had correct code for the task.  We would then take thirty minutes to examine their code, which the programmers did with test cases.  In several classes and with over a hundred programmers, the results varied little: ninety percent of the programmers found bugs in their programs (and I wasn’t always convinced of the correctness of the code in which no bugs were found).

I was amazed: given ample time, only about ten percent of professional programmers were able to get this small program right.  But they aren’t the only ones to find this task difficult: in the history in Section 6.2.1 of his Sorting and Searching, Knuth points out that while the first binary search was published in 1946, the first published binary search without bugs did not appear until 1962.

— Jon Bentley, Programming Pearls (1st edition), pp. 35-36.

Several hours!  Ninety percent!  Dude, SRSLY!  Isn’t that terrifying?

One of the reasons I’d like to see a copy of the Second Edition is to see whether this passage has changed — whether the numbers improved between 1986 and the Second-Edition date of 1999.  My gut tells me that the numbers must have improved, that things can’t be that bad;  yet logic tells me that in an age when programmers spend more time plugging libraries together than writing actual code, core algorithmic skills are likely if anything to have declined.  And remember, these were not doofus programmers that Bentley was working with: they were professionals at Bell Labs and IBM.  You’d expect them to be well ahead of the curve.

And so, the Great Binary Search Experiment

I would like you, if you would, to go away and do the exercise right now.  (Well, not right now.  Finish reading this article first!)  I am confident that nearly everyone who reads this blog is already familiar with the binary search algorithm, but for those of you who are not, Bentley’s description above should suffice.  Please fire up an editor buffer, and write a binary search routine.  When you’ve decided it’s correct, commit to that version.  Then test it, and tell me in the comments below whether you got it right first time.  Surely — surely — we can beat Bentley’s 10% hit-rate?

Here are the rules:

  1. Use whatever programming language you like.
  2. No cutting, pasting or otherwise copying code.  Don’t even look at other binary search code until you’re done.
  3. I need hardly say, no calling bsearch(), or otherwise cheating :-)
  4. Take as long as you like — you might finish, and feel confident in your code, after five minutes; or you’re welcome to take eight hours if you want (if you have the time to spare).
  5. You’re allowed to use your compiler to shake out mechanical bugs such as syntax errors or failure to initialise variables, but …
  6. NO TESTING until after you’ve decided your program is correct.
  7. Finally, the most important one: if you decide to begin this exercise, then you must report — either to say that you succeeded, failed or abandoned the attempt.  Otherwise the figures will be skewed towards success.

(For the purposes of this exercise, the possibility of numeric overflow in index calculations can be ignored.  That condition is described here but DO NOT FOLLOW THAT LINK until after writing your program, if you’re participating, because the article contains a correct binary search implementation that you don’t want to see before working on your clean-room implementation.)

If your code does turn out to be correct, and if you wish, you’re welcome to paste that code into your comment …  But if you do, and if a subsequent commenter points out a bug in it, you need to be prepared to deal with the public shame :-)

For extra credit: those of you who are really confident in your programming chops may write the program, publish it in a comment here and then test it.  If you do that, you’ll probably want to mention the fact in your comment, so we cut you extra slack when we find your bugs.

I will of course summarise the results of this exercise — let’s say, in one week’s time.

Let’s go!

Update (an hour and a half later)

Thanks for the many posted entries already!  I should have warned you that the WordPress comment system interprets HTML, and so eats code fragments like

if a[mid] < value

The best way to avoid this is to wrap your source code in {source}…{/source} tags, but using square brackets rather than curly.  (The first time I tried to tell you all this, I used literal square brackets, and my markup-circumvention instructions were themselves marked up — D’oh!).  Do not manually escape < and > as &lt; and &gt; — the {source} wrapper deals with these.  Doing it this way also has the benefit of preserving indentation, which no other method seems to do.

And an apology for WordPress: I really, really wish that this platform allowed commenters to preview their comments and/or edit them after posting, so that all the screwed-up source code could have been avoided.  I’ve tried to go and fix some of them myself, but — arrgh! — it turns out that WordPress not only displays code with < symbols wrongly, it actually throws away what follows, so there’s nothing for me to restore.

Update 2 (four hours after the initial post)

Wow, you guys are amazing.  Four hours, and this post already has more comments than the previous record holder (Whatever Happened to Programming, 206 comments at the time of writing.)

For anyone who’d like to see more discussion, there are some good comments at Hacker News and perhaps some slightly less insightful comments at Reddit, where actually writing code is seen as “elitism”.

Update 3: links to this whole series

 

1,011 responses to “Are you one of the 10% of programmers who can write a binary search?

  1. I have the second edition and can tell you that the section you quoted above is essentially unchanged. The reference to IBM and Bell Labs was replaced by the more general “I’ve assigned this problem in courses for professional programmers” but the numbers are still there.

  2. int binarySearch(int[] a, int value) {
    int low = 0;
    int high = a.length – 1;

    while (low <= high) {
    int mid = low + (high – low)/2;
    int midValue = a[mid];

    if (value midValue) {
    low = mid + 1;
    } else {
    return mid;
    }
    }

    return -1;
    }

    (not tested, typed in comment box)

  3. Gah, once I hit “put down this column and write the code yourself”, I did. Failed to read the rules that said don’t test it. So essentially, I failed by not reading specifications, which is probably just as bad.

  4. Implementation:

    <?php

    $a = array();
    $k = 10;
    for ($i = 0; $i < 500; $i++) {
    $k += rand(1,20);
    $a[] = $k;
    }

    foreach ($a as $v) {
    echo $v.' ';
    }
    echo "\n";

    for ($i = 0; $i $right) {
    return false;
    }
    $k = floor(($left+$right)/2.0);
    if ($array[$k] == $lookfor) {
    return $k;
    }
    if ($array[$k] > $lookfor) {
    return search($array, $left, $k-1, $lookfor);
    }
    return search($array, $k+1, $right, $lookfor);
    }
    ?>

    Correct on first run, according to the included test cases. Bug reports welcome.

  5. Seemed to work for a couple quick tests, but blew up when searching for something that wasn’t in the list.

    def bsearch_helper(list, target, low, hi):
    if low > hi:
    return None

    mid = (low + hi) / 2

    m = list[mid]

    c = cmp(m, target)

    if c == 0:
    return mid
    elif c < 0:
    return bsearch_helper(list, target, mid + 1, hi)
    else:
    return bsearch_helper(list, target, low, mid – 1)

    def bsearch(list, target):
    return bsearch_helper(list, target, 0, len(list))

  6. Hmmm, HTML fail. Let’s try again.

    int binarySearch(int[] a, int value) {
        int low = 0;
        int high = a.length – 1;
    
        while (low <= high) {
            int mid = low + (high – low)/2;
            int midValue = a[mid];
    
            if (value < midValue) {
                high = mid - 1;
            } else if (value > midValue) {
                low = mid + 1;            
            } else {
                return mid;
            }
        }
    
        return -1;
    }
    

    (not tested, typed in comment box)

  7. Code below for any more crowdsourced debugging:

    int search(int term, int * array, int size) {
    int mid = size / 2;
    if (array[mid] > term) return search(term, array, mid);
    if (array[mid] < term) return mid + search(term, array + mid, size – mid);
    return mid;
    }

  8. Andres Holzer-Torres

    Success. Altho I did it recursively.

    Has been tested:
    static int binarySearch(int[] values, int val) throws Exception
    {
    return binarySearchHelp(values, val, 0, values.length – 1);
    }

    static int binarySearchHelp(int[] values, int val, int start, int end)
    throws Exception
    {
    if (start > end)
    throw new Exception(“Somehow indexes have gotten reversed”);
    if (start == end)
    return values[start]==val ? start : -1;
    int mid = (start + end) / 2;
    if (values[mid] > val)
    return binarySearchHelp(values, val, start, mid);
    else if (values[mid] < val)
    return binarySearchHelp(values, val, mid + 1, end);
    else
    return values[mid]==val ? mid : -1;
    }

  9. def helper(array, low, high, x):
        if low > high:
            return False
        if low == high:
            return array[low] == x
        mid = (low + high) / 2
        if array[mid] == x:
            return mid
        elif array[mid] > x:
            helper(array, low, mid, x):
        else:
            helper(array, mid, high, x):
    
    def sort(array, x):
        helper(array, 0, len(array) - 1)
    
    
  10. @Josh: You sometimes return a boolean (False) and sometimes an integer (mid). Assuming you meant to return True instead of mid, you risk an infinite loop because you don’t guarantee that your interval gets smaller each step.

  11. Don’t forget to account for numbers outside the range of your sorted array. I forgot it in my first attempt, so searching for something less than the first element or greater than the last would result in an infinite loop!

    def find(list, n)
    mid = (list.size / 2).ceil
    target = list[mid]

    # Arg, I failed!
    return false if n > list.last || n target
    return find(list[mid,list.size],n)
    end
    if n < target
    return find(list[0,mid],n)
    end

    true
    end

    list = (0..1001).to_a

    puts find(list,500)
    puts find(list,list.first)
    puts find(list,list.last)
    puts find(list,1000)
    puts find(list,33)
    puts find(list,-1)
    puts find(list,1002)

  12. Code here:

    http://pastebin.com/ms6BYwyy

    I was not confident enough to post it without testing, and rightly so because I had a bug because I wrote the code in a way that seemed very elegant to me but that turned out to loop forever.

    The original comparison was:

    if (array[h] = x) hi = h + 1;

    Which would terminate the loop immediately if it so happened that array[h] == x, but which would lead to an infinite loop if the interval was 2 elements large with the first element smaller than the search value and the second element larger (as with my first test case).

    Time taken: 10 minutes.

  13. I wrote it, and when I was sure it worked, I tested it. Not a single correction had to be made. Recursive algorithms are easy to think. I would have probably failed writing an iterative version.

    bool binSearch(std::vector const &v, int key, int first, int last)
    {
    if (last – first == 1)
    return v[first] == key;

    int mid = (first+last)/2;
    if (key < v[mid])
    {
    return binSearch(v, key, first, mid);
    }
    else
    {
    return binSearch(v, key, mid, last);
    }
    }

  14. Argh, it seems smaller-than and greater-than signs are not escaped.

    That was supposed to read

    if (array[h] x) hi = h;

  15. I fail. Buggy as crap. I bring shame to professional programmers everywhere.

    #!/usr/bin/python
    
    def bfind(value, seq, start, end):
      print 'bfind(%s, %s, %s, %s)' % (value, seq, start, end)
      if start == end:
        if seq[start] == value:
          return start
        else:
          return None
      else:
        pivot = start + (end - start) / 2 
        middle = seq[pivot]
        print 'pivot=%s, middle=%s' % (pivot, middle)
        if middle == value:
          return pivot
        elif middle >= value:
          return bfind(value, seq, start, pivot - 1)
        else:
          return bfind(value, seq, pivot + 1, start)
    
    tests = [ 
        (3, range(5), 4), 
        (7, range(50), 6), 
        (26, range(50), 25),
        (99, range(50), None),
        (-1, range(50), None),
        (1, range(3), 2), 
        (1, [], None),
        ]   
    
    for value, seq, result in tests:
      print '-' * 40
      print 'looking for %s in %s elements' % (value, len(seq))
      print bfind(value, seq, 0, len(seq) - 1)
    
  16. int bs(int len, int array[len], int t)
    {
    int start = 0, end = len;
    while (start < end) {
    int m = (start + end) / 2;
    if (array[m] t) end = m;
    else return m;
    }
    return -1;
    }

  17. int binarySearch(int array[], int value, int low, int high)

    if(low > high)
    return -1;

    int midPoint = low + (high-low)/2;
    int midValue = array[midPoint];

    if(value == midValue) {
    return midPoint;
    }else if(value > midValue) {
    return binarySearch(array, value, midPoint+1, high);
    }else {
    return binarySearch(array, value, low, midPoint-1);
    }
    }

    // Didn’t test it. Just used pen and paper.
    // This could stackoverflow if the compiler
    // doesn’t support tail recursion.

  18. Wrote it in Emacs (with SLIME), committed (^X ^E), fixed one syntax error (LET -> LET*), then hand-tested with a few corner cases in the REPL. I think I got it right, but I feel strangely unconfident…

    (defun binary-search (array value &key (start 0) (end (length array)))
      "Return the index of value in the sorted array if it exists.  Otherwise return nil.  The search range is [start, end)."
      ;; Base-case 1: not found.
      (when (= start end)
        (return-from binary-search nil))
      (let* ((mid (floor (+ start
                            (/ (- end start)
                               2))))
             (mid-val (elt array mid)))
        (cond
          ;; Base-case 2: found.
          ((= value mid-val)
               mid)
          ;; Search left in range [start, mid).
          ((< value mid-val)
           (binary-search array value :start start :end mid))
          ;; Search right in range (mid, end).
          (t
           (binary-search array value :start (1+ mid) :end end)))))
    
  19. VB.NET – I did get it wrong the first time, I had the upper and lower reversed when I was checking the startpoint.

    Dim numarray(19) As Integer
    Dim t As Integer = 7

    numarray(0) = 1
    numarray(1) = 2
    numarray(2) = 3
    numarray(3) = 4
    numarray(4) = 5
    numarray(5) = 6
    numarray(6) = 7
    numarray(7) = 8
    numarray(8) = 9
    numarray(9) = 10
    numarray(10) = 11
    numarray(11) = 12
    numarray(12) = 13
    numarray(13) = 14
    numarray(14) = 15
    numarray(15) = 16
    numarray(16) = 17
    numarray(17) = 18
    numarray(18) = 19
    numarray(19) = 20

    Dim startpoint As Integer
    Dim lower = 0
    Dim upper = 19

    Do
    startpoint = (lower + upper) \ 2

    If numarray(startpoint) = t Then
    Debug.Print(“found it”)
    Exit Do
    End If

    If upper = lower Then
    Debug.Print(“not found”)
    Exit Do
    End If
    If numarray(startpoint) < t Then
    lower = startpoint
    Else
    upper = startpoint
    End If

    Loop

  20. @Lawrence Kesteloot I mean to return False if it’s not found, otherwise return the index where it is found, similar to how you return a -1. I’m still prepared to don my ribbons of shame.

  21. NO TESTING until after you’ve decided your “program is correct.”

    Who cares what the results are with this rule in place? You’re not measuring anything that applies to real world development.

    Next, you’ll tell me only 10% of programmers produce x lines of code per year.

  22. first posting messed up by html, trying again

    int bs(int len, int array[len], int t)
    {
        int start = 0, end = len;
        while (start < end) {
            int m = (start + end) / 2;
            if (array[m] < t) start = m + 1;
            else if (array[m] > t) end = m;
            else return m;     
        }
        return -1;
    }
  23. This was my first attempt. I haven’t discovered any bugs yet.

    def b_search_helper(array, target, high, low):
        middle = low + ((high - low) / 2)
        
        if array[middle] == target:
            return middle
        elif array[middle]  target:
            high = middle - 1
        
        if high < low:
            return -1
    
        return b_search_helper(array, target, high, low)
        
    
    def b_search(array, target):
        return b_search_helper(array, target, len(array) - 1, 0)
    
  24. int bsearch(int* h, int l, int n) {
        if (l <= 0) {
            return -1;
        } else {
            int m1 = l / 2;
            int m2 = l / 2 + 1;
    
            if (n == h[m1])       return m1;
            else if (n < h[m1]) return bsearch(h,m1,n);
            else                             return bsearch(h+m2,l-m2,n);
        }
    }
    

    Thought about this a little bit more, since you said I was likely to screw it up. I’ll test it after I submit, since that seems to be the spirit of the exercise.
    Hope I don’t blow it :)

  25. Patrick Shields

    Wrote a recursive solution in python with more or less no error checking. Worked until I changed from printing results to returning them and forgot to make the recursive calls return statements. Otherwise working fairly well. What am I missing?

    This probably won’t look pretty:
    def bsearch(listy, val, index):
    if len(listy) == 1:
    if listy[0] != val:
    print “ERRRRRRRRRRRRROOOOOOOOOOOOOOORRRRRRRRRRRRRRRRRRRRRR”
    return -1
    else:
    return index
    else:
    new_ind = len(listy)/2
    if listy[new_ind] == val:
    return index+new_ind
    elif listy[new_ind] < val:
    return(bsearch(listy[new_ind+1:], val, index+new_ind+1))
    else:
    return(bsearch(listy[:new_ind], val, index))

  26. @Josh: If you want to return False, then the case of “low == high” is wrong because it returns “array[low] == x”. I think you can just remove that case altogether as long as you fix the recursion parameters.

    @Daniel: Looks good.

  27. ##Woohoo!! Got right the first time:)
    def bsearch(arr,key,start=0,end=None):
    if end == None: end = len(arr) – 1
    if start > end: return None
    if start == end and arr[start] != key: return None

    mid = (start+end)/2
    if arr[mid] == key:
    return mid
    if arr[mid] > key:
    return bsearch(arr,key,start,mid-1)
    if arr[mid] < key:
    return bsearch(arr,key,mid+1,end)

  28. Don’t forget to account for numbers outside the range of your sorted array. I forgot it in my first attempt, so searching for something less than the first element or greater than the last would result in an infinite loop!

    def find(list, n)
    	mid = (list.size / 2).ceil
    	target = list[mid]
    
    	# Arg, I failed!
    	return false if n > list.last || n < list.first
    
    	if n > target
    		return find(list[mid,list.size],n) 
    	end
    	if n < target
    		return find(list[0,mid],n)
    	end
    
    	true
    end
    
    list = (0..1001).to_a
    # these should be true:
    puts find(list,500)
    puts find(list,list.first)
    puts find(list,list.last)
    puts find(list,1000)
    puts find(list,33)
    
    # these should be false:
    puts find(list,-1)
    puts find(list,1002)

    Note: Duh, fixed for html

  29. Time taken: 8 minutes

    Result: failed. My original attempt had “end = mid – 1” instead of “end = mid”. That’s the only change I made from the original version.

    Total testing/fixing time: 5 minutes

    So 13 minutes total. I wouldn’t be horribly surprised if there are more bugs in there…

    int bsearch_int( const int * values, const size_t n, const int key )
    {
    int begin = 0;
    int end = n;

    while( begin < end )
    {
    const int mid = begin + (end-begin)/2;
    const int midval = values[mid];
    if( key midval )
    begin = mid + 1;
    else
    return mid;
    }

    return -1;
    }

  30. def bsearch(t, arr, a=0, b=arr.length-1)
      while a <= b
        m = (a+b)/2
        x = arr[m]
    
        if x == t
          return m
        elsif x > t
          b = m-1
        else # x < t
          a = m+1
        end
      end
    
      return false
    end
    

    a and b were initialised in the body but then I saw Lisp version :)

    Ruby and other high-level languages are like high level pseudocode so that’s why it’s easy (but you can trip yourself if you don’t test carefully). Hope I got it right :)

  31. I wasn’t going to bother posting, but since the only Python versions so far are recursive, here’s a really awful “whipped up in five minutes” iterative version. Hopefully, WP won’t mangle this too badly.

    def bsearch(range, target):
        crange = range
        offset = 0
        while True:
            ctarget = len(crange) / 2
            if crange[ctarget] == target:
                return ctarget + offset
            elif crange[ctarget] > target:
                crange = crange[:ctarget]
            else:
                crange = crange[ctarget+1:]
                offset += ctarget+1
            if len(crange) == 0:
                return -1
  32. Well. First go, I wrote this:

    long *binsearch( long *ary, long sz, long item)
    {
    if( 0 == sz ) return NULL; /* 0-length array */
    if( ary[ sz>>1 ] == item ) return &ary[ sz>>1 ];
    if( ary[ sz>>1 ] >1 ], sz>>1 + sz&1 – 1, item );
    return binsearch( ary, sz>>1, item );
    }

    I was convinced that the logic was sound. C disagreed. Not being a daily user of C, it took me a while to figure it out… for reference, here’s the corrected version:

    long *binsearch( long *ary, long sz, long item)
    {
    if( 0 == sz ) return NULL; /* 0-length array */
    if( ary[ sz>>1 ] == item ) return &ary[ sz>>1 ];
    if( ary[ sz>>1 ] >1 ], sz>>1 + (sz&1) – 1, item );
    return binsearch( ary, sz>>1, item );
    }

  33. Python indentation is wrong in the previous submission!!!

    I have put up my code at


    def bsearch(arr,key,start=0,end=None):
    if end == None: end = len(arr) 1
    if start > end: return None
    if start == end and arr[start] != key: return None
    mid = (start+end)/2
    if arr[mid] == key:
    return mid
    if arr[mid] > key:
    return bsearch(arr,key,start,mid1)
    if arr[mid] < key:
    return bsearch(arr,key,mid+1,end)

    view raw

    bsearch.py

    hosted with ❤ by GitHub

  34. Erm, nevermind, looks like someone else already posted one. :)

  35. int bsearch( int v[ ], int s, int e )
    {
        int a = 0, b = s - 1, i;
    
        do
        {
            i = ( b + a + 1 ) / 2;
    
            if( v[ i ] > e )  b = i;
            else  a = i;
        }
        while( v[ i ] != e && a < b );
    
        if( v[ i ] == e )  return i;
    
        return -1;
    }

    Tested with first/middle/last element in even-/odd-size arrays. Works as far as I can tell.

  36. python example, only cursory testing so far so be gentle:

    def bsearch(lst, item):
    bottom, top = 0, len(lst)

    while top – bottom >= 3:
    mid = (top + bottom) // 2

    c = cmp(item, lst[mid])

    if c 0:
    bottom = mid + 1
    else:
    return True

    if item == lst[bottom]:
    return True

    return top – bottom == 2 and item == lst[bottom + 1]

  37. I’ll throw in my extremely unoptimized php version, just to represent the web dudes… (And yes, I tested it)

    2)
    {
    $iMiddle = round($iArraySize/2);
    }
    elseif($iArraySize == 2)
    {
    $iMiddle = 0;
    }
    else
    {
    $bQuit = true;
    print “\nRan out of values, is the answer: $aItems[0]?”;
    exit();
    }

    $iTestValue = $aItems[$iMiddle];

    print “Testing round $i”;
    print “\nArray size: ” . $iArraySize;
    print “\nMiddle of Array: ” . $iMiddle;
    print “\nLooking for Value ” . $iVal;
    print “\nFound Value: ” . $iTestValue;

    if($iTestValue == $iVal)
    {
    //yay we found it!
    print “\nYay they match!”;
    $bQuit = true;
    }
    else
    {
    if($iArraySize > 2)
    {
    if($iTestValue > $iVal)
    {
    $aItems = array_slice($aItems, 0, $iMiddle);
    }
    else
    {
    $aItems = array_slice($aItems, $iMiddle );
    }
    }
    else
    {
    $aItems = array($aItems[1]);
    }
    }

    }

    ?>

  38. Damn. HTML munched my code. :(

  39. Untested (but compiled). Scrolling down to the comment box, I couldn’t help but glance at the other submissions. Fortunately, due to their similarity, that only increased my faith in my own attempt. Perhaps it would be best if they were somehow hidden for the next week, though…

    I wish there were a comment preview – no idea whether this will be formatted correctly.

    	static public <T extends Comparable> int binarySearch(List haystack, T needle)
    	{
    		int min = 0;
    		int max = haystack.size() - 1;
    		while(max >= min)
    		{
    			int next = (min + max) / 2;
    			int cmp = needle.compareTo(haystack.get(next));
    			if(cmp == 0)
    				return next;
    			if(cmp < 0)
    				max = next - 1;
    			else
    				min = next + 1;
    		}
    		return -1;
    	}
    
  40. 2)
    {
    $iMiddle = round($iArraySize/2);
    }
    elseif($iArraySize == 2)
    {
    $iMiddle = 0;
    }
    else
    {
    $bQuit = true;
    print “\nRan out of values, is the answer: $aItems[0]?”;
    exit();
    }

    $iTestValue = $aItems[$iMiddle];

    print “Testing round $i”;
    print “\nArray size: ” . $iArraySize;
    print “\nMiddle of Array: ” . $iMiddle;
    print “\nLooking for Value ” . $iVal;
    print “\nFound Value: ” . $iTestValue;

    if($iTestValue == $iVal)
    {
    //yay we found it!
    print “\nYay they match!”;
    $bQuit = true;
    }
    else
    {
    if($iArraySize > 2)
    {
    if($iTestValue > $iVal)
    {
    $aItems = array_slice($aItems, 0, $iMiddle);
    }
    else
    {
    $aItems = array_slice($aItems, $iMiddle );
    }
    }
    else
    {
    $aItems = array($aItems[1]);
    }
    }

    }

    ? >

    Let’s try that again…

  41. Okay, it doesn’t. Screws up for values less than the array minimum. Ah, well.

  42. long *binsearch( long *ary, long sz, long item)
    {
            if( 0 == sz ) return NULL; /* 0-length array */
            if( ary[ sz>>1 ] == item ) return &ary[ sz>>1 ];
            if( ary[ sz>>1 ] >1 ], sz>>1 + (sz&1) - 1, item );
            return binsearch( ary, sz>>1, item );
    }
    
  43. Andrew Queisser

    That was fun – I took about 10 minutes and couldn’t wait to try it so I failed the test. I’m in the 90%. My submission has two problems, it compares the value to the index instead of the value and it doesn’t terminate when the value isn’t there. Both problems were easily fixed once I identified them.

    #include
    #include

    static int data[10000];

    static int load(char *fname)
    {
    FILE *fp = fopen(fname, “r”);
    int dc = 0;
    int *dp = data;
    if (fp)
    {
    while (fscanf(fp, “%d”, dp) > 0)
    dp++;
    dc = dp-data;
    printf(“read %d values\n”, dc);
    }

    return dc;
    }

    int main(int argc, char **argv)
    {
    int target = atoi(argv[2]);
    int count = load(argv[1]);
    int bot = 0;
    int top = count-1;
    int pivot;
    int found = 0;

    if (count == 0)
    return -1;

    while (!found && bot != top)
    {
    pivot = (bot+top)/2;
    printf(“[%d] %d [%d] %d [%d] %d\n”, bot, data[bot], pivot, data[pivot], top, data[top]);
    if (target > data[pivot])
    bot = pivot;
    else if (target < data[pivot])
    top = pivot;
    else
    {
    printf("found %d at index %d\n", target, pivot);
    found = 1;
    }
    }

    return found;
    }

  44. I attempted and failed. I instinctively hit the “Run” button before I was actually done, because it’s so ingrained to test the code at each step.

  45. How do we know if we pass?

  46. [ int BinarySearch(int value,int low, int hi, int[] list) {
    var middle=((hi-low) / 2)+low;

    if (hi<=low||list[middle]==value) {
    return (list[middle]==value ? middle : -1);
    }
    if(list[middle]value) {
    return BinarySearch(value, low, middle-1, list);
    }
    return -1;
    }]

  47. Most of my previous comment got eaten. This is what worked for me.

    def b_search_helper(array, target, high, low):
        middle = low + ((high - low) / 2)
        
        if array[middle] == target:
            return middle
        elif array[middle] < target:
            low = middle + 1
        elif array[middle] > target:
            high = middle - 1
        
        if high < low:
            return -1
    
        return b_search_helper(array, target, high, low)   
    
    def b_search(array, target):
        return b_search_helper(array, target, len(array) - 1, 0)
    
  48. Bugfixing

    int
    myBSearch (int* h, int l, int n)
    {
      if (l <= 0) {
        return -1;
      } else {
        int m1 = l / 2;
        int m2 = l / 2 + 1;
    
        if (n == h[m1]) {
          return m1;
        } else if (n < h[m1]) {
          return myBSearch(h,m1,n);
        } else {
          int r = myBSearch(h+m2,l-m2,n);
          if (r == -1) {
            return -1;
          } else {
            return m2 + r;
          }
        }
      }
    }
    
  49. Sorry, don’t count mine, I did test it. I recognise I am not able to “code on paper”, that’s no news for me :)

  50. Well, mine turned out a bit wordier than a lot of other people’s… bad programmer! (and to think I taught myself coding in the age of 16K machines with cassette drives… I’ve gotten fat and lazy.)

    Like many others, I return the index location if it’s found and -1 if it’s not.

    Thought of doing it recursively, which would be more elegant, but just went for the most straightforward brute-force approach. Hopefully, it actually works for all cases.

    java.lang.String items[]=new String[50];//0 indexed
    int arraylength=items.length-1;
    boolean found=false;
    int lowbound=0;
    int midpoint=-1;
    int highbound=arraylength;
    int foundIndex=-1;
    for(int i=0;i<50;i++)
    {
    items[i]=String.valueOf(i);
    }
    Arrays.sort(items);
    String item2find=new String("30");
    int passes=0;
    while(!found)
    {
    //first, see if object can exist in range
    passes++;
    if ((item2find.compareTo(items[lowbound])0))
    {
    found=true;
    foundIndex=-1;
    continue;
    }
    midpoint=lowbound+((highbound-lowbound)/2);

    if(items[midpoint].equals(item2find))
    {
    found=true;
    foundIndex=midpoint;
    continue;
    }
    if(highbound==lowbound)
    {
    found=true;
    foundIndex=-1;
    continue;
    }
    if(item2find.compareTo(items[midpoint])<0)
    {
    highbound=midpoint-1;
    }
    else
    {
    lowbound=midpoint+1;
    }
    }
    System.out.println(passes+" passes. Location:"+foundIndex);

    In terms of "Worked on first try"… well, sortakinda. :) I needed to add arrays.sort. Once the array was, in fact, sorted, the algorithm SEEMS to work. If I put in an element that's not there, it returns -1, if I put in an element that is there, it finds it. I'm sure I'm missing something, I always do….


  51. from random import randint
    def bsearch(stuff, x, offset=0):
    if len(stuff) == 0:
    return 1
    half = int(len(stuff) / 2)
    if stuff[half] == x:
    return half + offset
    elif x < stuff[half]:
    return bsearch(stuff[:half], x, offset)
    elif x > stuff[half]:
    return bsearch(stuff[half + 1:], x, offset + half + 1)
    def test(n=100, m=200):
    xs = [randint(0, n) for i in range(m)]
    xs.sort()
    y = randint(0, n)
    print ">>>", y
    i = bsearch(xs, y)
    if (i == 1 and y in xs) or (i != 1 and xs[i] != y):
    print "error ——————-"
    print y, i
    print xs
    print "————————-"
    else:
    print "ok"
    for i in range(100):
    test()

    view raw

    gistfile1.pyw

    hosted with ❤ by GitHub

    lo and behold, I run the test for the first time and… voila! here’s a bug!

    I must admit it was a pure WTF moment, but the error turned out to be a typo — the program still parsed and ran, it just… well, didn’t work. ;)

  52. Ah, screw it.

    long *binsearch( long *ary, long sz, long item)
    {
    if( 0 == sz ) return NULL; /* 0-length array */
    if( ary[ sz>>1 ] == item ) return &ary[ sz>>1 ];
    if( ary[ sz>>1 ] < item ) return binsearch( &ary[ 1 + sz>>1 ], sz>>1 + sz&1 – 1, item );
    return binsearch( ary, sz>>1, item );
    }

  53. def bsearch(target,array,offset=0):
    l = len(array)
    if l == 0:
    return False
    elif l == 1:
    if array[0] == target:
    return offset
    else:
    return False
    else:
    midpos = l//2
    midval = array[midpos]
    if midval == target:
    return offset + midpos
    elif midval < target:
    return bsearch(target,array[midpos:],offset+midpos)
    else:
    return bsearch(target,array[:midpos],offset)

  54. Again with formatting

    def bsearch(target,array,offset=0):
        l = len(array)
        if l == 0:
            return False
        elif l == 1:
            if array[0] == target:
                return offset
            else:
                return False
        else:
            midpos = l//2
            midval = array[midpos]
            if midval == target:
                return offset + midpos
            elif midval < target:
                return bsearch(target,array[midpos:],offset+midpos)
            else:
                return bsearch(target,array[:midpos],offset)
    
  55. Bah, markup ate my attempt. Here’s how it’s supposed to read: http://pastebay.com/94342

    @mike, your instructions on how to circumvent wordpress’ markup got marked up itself, so it can’t be read…. :)

  56. Mooneer Salem

    #include
    #include

    int binary_search_helper(int *array, int search_from, int search_to, int needle)
    {
    int subset_length = search_to – search_from + 1;
    int midpoint = search_from + subset_length / 2;

    if (search_from == search_to)
    {
    if (needle == array[search_from]) return search_from;
    return -1;
    }
    if (needle == array[midpoint]) return midpoint;

    if (needle < array[midpoint] && midpoint != search_from) return binary_search_helper(array, search_from, midpoint – 1, needle);
    else if (needle > array[midpoint] && midpoint != search_to) return binary_search_helper(array, midpoint + 1, search_to, needle);
    else return -1;
    }

    int binary_search(int *array, int length, int needle)
    {
    return binary_search_helper(array, 0, length – 1, needle);
    }

    int main()
    {
    int array_odd[] = {1, 3, 4, 7, 9, 11, 102};
    int array_even[] = {1, 3, 4, 7, 8, 9, 11, 102};
    int index;

    for (index = 0; index < 7; index++)
    {
    assert(binary_search(array_odd, 7, array_odd[index]) == index);
    }

    assert(binary_search(array_odd, 7, 1337) == -1);
    assert(binary_search(array_odd, 7, -5) == -1);

    for (index = 0; index < 8; index++)
    {
    assert(binary_search(array_even, 8, array_even[index]) == index);
    }

    assert(binary_search(array_even, 8, 1337) == -1);
    assert(binary_search(array_even, 8, -5) == -1);

    printf(“All tests pass.\n”);
    return 0;
    }

    Output:

    mooneer@voldemort:~$ ./bsearch
    All tests pass.
    mooneer@voldemort:~$

  57. After 15 minutes: http://gist.github.com/371501

    I wouldn’t be quite this defensive if I didn’t know of the problem’s reputation.

    I haven’t bothered with local variables or default arguments.

  58. So, my results? Well, I failed. I got the logic right, but got bitten by an arcane syntactical rule… because I chose a language I could test easily, but in which I am not quite fluent.

    In fairness, though, it’s five years since I coded for a living.

  59. pete gamache

    My first crack at bsearch in Perl, and it seems to work. Compares strings only, for brevity.


    # bsearch($elt, $arrayref) performs a binary search on an array of
    # sorted strings. returns element index if $elt is in @$arrayref,
    # undef otherwise.
    sub bsearch {
    my ($elt, $arrayref, $min_idx, $max_idx) = @_;
    my $nelts = scalar @$arrayref;
    return undef unless $nelts > 0;
    return undef if $min_idx > $max_idx;

    $min_idx = 0 unless defined $min_idx;
    $max_idx = $nelts-1 unless defined $max_idx;
    my $mid_idx = int(($max_idx - $min_idx) / 2) + $min_idx;

    if ($elt eq $arrayref->[$mid_idx]) {
    return $mid_idx;
    }
    elsif ($elt lt $arrayref->[$mid_idx]) {
    return bsearch($elt, $arrayref, $min_idx, $mid_idx-1);
    }
    else {
    return bsearch($elt, $arrayref, $mid_idx+1, $max_idx);
    }
    }

  60. Mooneer Salem

    Gah, it stripped out the pre tag. http://pastebin.ca/1868406

    Output is still “All tests pass.” :)

  61. Unfortunately, i failed because i swapped a < for a <= accidently.

  62. Steffen Beyer

    First try:

    # supposed to return the index of target in list
    def bsearch(list, target)
      loop do
        return nil if list.empty?
    
        middle = (list.length / 2).floor
        left, pivot, right = list[0 .. middle - 1],
                             list[middle],
                             list[middle + 1 .. list.length - 1]
    
        if pivot == target
          return middle
        elsif pivot < target
          list = right
        elsif pivot > target
          list = left
        else
          warn 'nanu?'
        end
      end
    end
    

    After short testing:

    # supposed to return the index of target in list
    def bsearch(list, target)
      base = 0
    
      loop do
        return nil if list.empty?
    
        middle = (list.length / 2).floor
        left, pivot, right = list[0 .. middle - 1],
                             list[middle],
                             list[middle + 1 .. list.length - 1]
    
        if pivot == target
          return middle + base
        elsif pivot < target
          list = right
          base = middle + 1
        elsif pivot > target
          list = left
        else
          warn 'nanu?'
        end
      end
    end
    
  63. Chester Grant

    public static int search(int [] arr, int key){
    if((arr.length == 0)||(arr ==null)){
    return -1;
    }
    if(arr.length ==1){
    if(arr[0] == key){
    return 0;
    }else{
    return -1;
    }
    }
    int left =0;
    int right = arr.length-1;
    int mid = (left+ right)/2;
    boolean found = false;
    while((left key){
    left =mid+1;
    mid = ((left+right)/2);
    }else{
    right = mid-1;
    mid = ((left+right)/2);

    }
    }

    if(found == false){
    return -1;
    }
    return mid;

    }

  64. untested, php, w/ tail-recursion

    recursion makes things like this easy

    $mB) return 1;
    return -1;
    }

    function binSearch($aData, $mSearchVal, $iStart = -1, $iEnd = -1) {
    if ($iStart == -1) { $iStart = 0; $iEnd = count($aData) -1; }

    switch($iEnd – $iStart) {
    case 1:
    if (0 == compare($aData[$iEnd], $mSearchVal)) return $iEnd;
    case 0:
    if (0 == compare($aData[$iStart], $mSearchVal)) return $iStart;
    return false;
    default:
    $iMidP = ($iStart + $iEnd) / 2;
    if (0 == compare($aData[$iMidP], $mSearchVal)) return $iMidP;
    if (0

  65. in python (no testing done):

    def search(n, l):
    H = len(l) – 1
    L = 0
    M = int(H / 2)

    while H – L > 0 and n != l[M]:
    if n > l[M]:
    L = M
    else:
    H = M
    M = int((H + L) / 2)

    if n == l[M]:
    return M

    return -1

  66. Just a simple, recursive Ruby solution. Hopefully correct too :-)

    #!/usr/bin/env ruby
    
    def search(search_item, array, middle = array.size / 2, middle_val = array[middle])
      return nil if search_item < array.first or search_item > array.last
      return middle_val if middle_val.eql?(search_item)
      search search_item, middle_val > search_item ? array[0,middle] : array[middle..-1]
    end
  67. Robin Message

    C, 10 minutes, no testing, fingers crossed…

    [
    int binary_search(int target,int* list,int start,int end) {
    if(start==end)return -1;
    if(list[start]==target)return start;
    int mid=start+(end-start)/2;
    if(list[mid]<target)return binary_search(target,list,mid+1,end);
    else return binary_search(target,list,start,mid+1);
    }

    int bsearch(int target,int* list,int len) {
    return binary_search(target,list,0,len);
    }
    ]

  68. Wrote it first, tested it afterwards, posting finally. As far as I can test it, no bugs found, and recognizes unavailable elements correctly. *phew*. Wrote and checked the code in 20 minutes or so. I Used Java List instead of arrays, since it’s easier to sort in my tests. But there’s no magical properties…

    Did it recursively. Returns the index in which the element is, or throws a suitable exception if not found.

    public static <T extends Comparable> int binarySearch(final T needle,
    final List haystack) throws ElementNotFoundException {
    return binarySearch(needle, haystack, 0, haystack.size() – 1);
    }

    private static <T extends Comparable> int binarySearch(final T needle,
    final List haystack, final int left, final int right)
    throws ElementNotFoundException {

    if (left > right) {
    throw new ElementNotFoundException();
    }

    final int center = ((right – left) / 2) + left;
    final T elementAtCenter = haystack.get(center);

    final int comparisonResult = elementAtCenter.compareTo(needle);

    if (comparisonResult 0) {
    return binarySearch(needle, haystack, left, center – 1);
    } else {
    return center;
    }
    }

  69. Haven’t tested it at all:

    	int somethign (unsigned int haystack[], unsigned int size, unsigned int needle) 
    	{
    		int lowerbound = 0;
    		int upperbound = size;
    
    		if (size == 0);
    			return INVALIDPARAMS;
    
    		for (;;) {
    			int middle = ((upperbound + lowerbound)/2);
    
    			if (needle == haystack[middle]) {
    				return middle;
    			}
    			
    			if (upperbound == lowerbound)
    			{
    				return NOTFOUND;
    			} 
    			
    			if (needle &lt; haystack[middle]) {
    				upperbound = middle;
    			} else {
    				lowerbound = middle + 1;
    			}
    		}
    
    		return NOTFOUND;
    	}
    
  70. I screwed it up. I tried to anticipate Python and integer-division rules (when ignoring them does the right thing instead), and so got infinite recursion.

    Two minutes longer than it should have taken me. Blargh.

  71. Ngh… formatting fail. Sorry about that. I didn’t know how to format the code properly :(

  72. No testing done, except for making it compile right. Bash away :)

    int * bsearch(int * begin, int * end, int v)
    {
      if (end - begin == 1)
        return (*begin == v) ? begin : (int*)0;
    
      int * middle = begin + (end - begin) / 2;
      if (*middle > v)
        return bsearch(begin, middle, v);
      else
        return bsearch(middle, end, v);
    }
    
    int main()
    { // usage:
      int array[] = { 1, 2, 4, 8, 16 };
      int v = 3;
    
      bsearch(array, array + 5, v);
      return 0;
    }
    
  73. http://www.mistcat.com/binary_search.html
    I can’t figure out your crazy wordpress code insertion magic…

  74. php, w/ tail-recursion, now tested, worked first time

    recursion makes things like this easy

    previous html formatting fail

    <?php
    function compare($mA, $mB) {
    //modify as necessary comparing
    if ($mA == $mB) return 0;
    if ($mA > $mB) return 1;
    return -1;
    }

    function binSearch($aData, $mSearchVal, $iStart = -1, $iEnd = -1) {
    if ($iStart == -1) { $iStart = 0; $iEnd = count($aData) -1; }

    switch($iEnd - $iStart) {
    case 1:
    if (0 == compare($aData[$iEnd], $mSearchVal)) return $iEnd;
    case 0:
    if (0 == compare($aData[$iStart], $mSearchVal)) return $iStart;
    return false;
    default:
    $iMidP = ($iStart + $iEnd) / 2;
    if (0 == compare($aData[$iMidP], $mSearchVal)) return $iMidP;
    if (0 < compare($aData[$iMidP], $mSearchVal))
    return binSearch($aData, $mSearchVal, $iStart, $iMidP - 1);
    return binSearch($aData, $mSearchVal, $iMidP + 1, $iEnd);
    }
    }

    var_dump(binSearch(array(0, 5, 22, 412, 1234, 2134, 5432), 5));

    ?>

  75. time taken: 8 minutes in ruby
    bugs: at least 2 illuminated from comments above.

    i guess i haven’t written a binary search since i was in algorithms class 15 years ago. no recollection as to whether that one turned out to be correct,.

    at least i failed quickly?

  76. If I wrote this right, it has the added advantage of always returning the smallest correct index (so a search for 3 in [2, 3, 3, 3, 3, 3, 3, 4] will return 1).

    int binsearch(int *arr, int len, int search) {
    int *mid, at, left_len, right_len, val;
    if (len <= 1) {
    if (len == 1 && *arr == search) return 0;
    return -1;
    }
    left_len = len/2;
    right_len = len – left_len;
    mid = arr + left_len;
    val = *mid;
    if (search <= val) {
    at = binsearch(arr, left_len, search);
    if (at = 0) at += left_len;
    }
    return at;
    }

  77. Python. I did test it, but I swear that I did not alter it after testing.

    def search(needle, l):
        start, end = 0, len(l)-1
    
        while start <= end:
            mid = (start + end) / 2
            if l[mid] == needle:
                return mid
            elif l[mid] < needle:
                start = mid + 1
            else:
                end = mid - 1
        return -1
    
  78. Python, iterative, passed all my tests so far (I’m probably missing something, though):


    def bsearch(nums, item):
    while nums:
    mid = len(nums) / 2

    if nums[mid] > item:
    nums = nums[:mid]

    elif nums[mid] < item:
    nums = nums[mid+1:]

    else:
    return True

    return False

    I predict that more than 10% of programmers who read programming blogs will get this right.

  79. 
        /**
         * Searches an array sorted according to the given comparator for an element
         * that is equal to obj according to the comparator, and returns the index
         * of the matching element. If no such element exists, then returns -1. If
         * more than one such element exists, then the index of any of the matching
         * elements may be returned. If the array is not sorted according the the
         * comparator, then false negatives may occur, but never false positives.
         * 
         * Does not support null as any argument or any element of the supplied
         * array.
         * 
         * @param <T>
         * @param array
         * @param obj
         * @param comparator
         * @return
         */
        public static <T> int binarySearch(T[] array, T obj, Comparator<? super T> comparator) {
            int low = 0;
            int high = array.length - 1;
            while(high > low) {
                int mid = (low+high)/2;
                T midObj = array[mid];
                int comparison = comparator.compare(obj, midObj);
                if(comparison < 0) {
                    high = mid - 1;
                }
                else if(comparison > 0) {
                    low = mid + 1;
                }
                else {
                    return mid;
                }
            }
            return -1;
        }
    
    
  80. def binary_search(array,val,lowval=0,highval=array.size-1)
    searchval = lowval + (highval-lowval)/2
    if val > array[searchval]
    lowval = searchval
    binary_search(array,val,lowval,highval)
    elsif val < array[searchval]
    highval = searchval
    binary_search(array,val,lowval,highval)
    else
    puts searchval
    end
    end

  81. Untested:

    template<typename raIterator, typename ltComparable>
    bool bsearch(raIterator begin, raIterator end, const ltComparable &val)
    {
        while(begin != end)
        {
            raIterator mid = (end - begin) / 2;
    
            if(*mid == val)
            {
                return true;
            }
    
            if(*mid < val)
            {
                begin = mid + 1;
            }
            else
            {
                end = mid;
            }
        }
    
        return false;
    }
  82. Emacs Lisp. No bugs found during testing.

    (defun bsearch (vec elt)
    “binary search VEC for ELT, returning index, or -1 if not found”
    (bsearch-impl vec elt 0 (- (length vec) 1)))
    (defun bsearch-impl (vec target left right)
    (let ((mid (/ (+ left right) 2)))
    (cond ((eq left right)
    (if (eq target (elt vec left))
    left
    -1))
    (( (elt vec mid)
    (bsearch-impl vec target (+ 1 mid) right)))))

  83. Andres Holzer-Torres


    static int binarySearch(int[] values, int val) throws Exception
    {
    return binarySearchHelp(values, val, 0, values.length 1);
    }
    static int binarySearchHelp(int[] values, int val, int start, int end)
    throws Exception
    {
    if (start > end)
    throw new Exception("Somehow indexes have gotten reversed");
    if (start == end)
    return values[start]==val ? start : -1;
    int mid = (start + end) / 2;
    if (values[mid] > val)
    return binarySearchHelp(values, val, start, mid);
    else if (values[mid] < val)
    return binarySearchHelp(values, val, mid + 1, end);
    else
    return values[mid]==val ? mid : -1;
    }

    Silly HTML eating my formatting . . . Still a success tho’.

  84. Another one with Javascript, recursive.

    function bsearch(arry, item, beg, end)
    {
    	var mid = Math.floor((end-beg)/2) + beg;
    
    	// found it
    	if(arry[mid] == item)
    		return mid;
    
    	// dead end, and not found
    	if(mid == beg)
    		return -1;
    
    	// check "the orher side"
    	if(arry[mid] > item)
    		return bsearch(arry, item, beg, mid);
    	else
    		return bsearch(arry, item, mid, end);
    }
    

    After writing it, I did some rough testing and seems OK (but I may be wrong).

    The surprising part it’s I’ve chosen Javascript. I need to think about it.

  85. def binsearch(arr, val)
    
    	return nil if arr.empty?
    
    	lo = 0
    	hi = arr.size - 1
    
    	while true
    
    		if hi - lo  val
    			hi = center
    		elsif arr[center] < val
    			lo = center
    		end
    	end
    
    end
    
  86. Robin Message

    Tried to be too clever,

    int mid=start+(end-start)/2;
    if(list[mid]==target)return mid;
    else if(list[mid]<target)return binary_search(target,list,mid+1,end);
    else return binary_search(target,list,start,mid);
    

    doesn’t go into an infinite loop…

  87. [
    def bsearch(sorted_array, given)
    midpoint = sorted_array.length / 2
    check = sorted_array[midpoint]
    return check if given == check
    return nil if sorted_array.length <= 1
    unless given < check
    bsearch sorted_array[(midpoint + 1)…sorted_array.length], given
    else
    besearch sorted_arry[0…midpoint], given
    end
    ]

  88. 2nd attempt, possibly with indentation this time. Python, iterative, passed all my tests so far.

    def bsearch(nums, item):
      while nums:
        mid = len(nums) / 2
    
        if nums[mid] > item:
          nums = nums[:mid]
    
        elif nums[mid] < item:
          nums = nums[mid+1:]
    
        else:
          return True
    
      return False
    

    I predict that more than 10% of programmers who read programming blogs will get this right. Apart from the formatting in comments.

  89. I think it ate part of my post.

  90. Here’s an updated post now that I can read how to mark up the code s.t. wordpress is happy.

    Time taken: 8 minutes

    Result: failed. My original attempt had “end = mid – 1″ instead of “end = mid”. That’s the only change I made from the original version.

    Total testing/fixing time: 5 minutes

    So 13 minutes total. After the off-by-one I mentioned above was fixed, it passed my tests and the other regression tests other people have posted in C… but still, my first version was a failure, so I fail the test. :)

    int binary_search( const int * values, const size_t n, const int key )
    {
      int begin = 0;
      int end = n;
    
      while( begin &lt; end )
      {
        const int mid = begin + (end-begin)/2;
        const int midval = values[mid];
    
        if( key  midval )
          begin = mid + 1;
        else
          return mid;
      }
    
      return -1;
    }
    

  91. /**
    * Returns index if found, -1 otherwise.
    */
    int binarySearch(int[] array, int searched) {
    int start = 0, end = array.length;
    while (start searched) begin = middle + 1;
    else end = middle - 1;
    }
    return -1;
    }

    Took me about half an hour. I’m in second year computer science and was introduced the algorithm last year, tough I never wrote one.

  92. For fun, I wrote mine in Delphi:

    // return index of v in a, or -1 if not found
    function Search(a: TArray<Integer>; v: Integer): Integer;
    var
      l, r, m: Integer;
    begin
      l := 0;
      r := Length(a);
      while l < r do
      begin
        m := l + (r - l) div 2;
        // m < r, guaranteed => bad index won't happen
        if a[m] = v then
          Exit(m);
        if v < a[m] then
          r := m
        else
          l := m + 1;
      end;
      Result := -1;
    end;
    
  93. looked again, forgot to enforce typing
    change line 22:
    $iMidP = ($iStart + $iEnd) / 2;
    to:
    $iMidP = (int)(($iStart + $iEnd) / 2);

  94. def binsearch(v, lst):
    “””Find a value v in a sorted list lst, using a binary search algorithm.
    “””
    length = len(lst):
    if length == 0:
    return False
    elif length == 1:
    if lst[0] == v:
    return True
    else:
    return False
    else :
    if not length%2:
    tmp = length/2 + 1
    else:
    tmp = length/2
    tmp_v = lst[tmp]
    if tmp_v == v:
    return True
    if tmp_v < v:
    return binsearch(v, lst[tmp:])
    else:
    return binsearch(v, lst[:tmp])

    This was my original version, and I've found 1 bug, my +1 is on the wrong branch.

    The no testing clause was a killer on this, because I'd normally do something like this with unit tests via TDD. So, I did not get a working version in my first cut, but would have if I had written it as I normally write code.

  95. I had an off-by-one error for the new upper/lower bound, but other than that, I got it.

  96. (ok the hmtl code tag was a bad idea apparently)

    /**
    * Returns index if found, -1 otherwise.
    */
    int binarySearch(int[] array, int searched) {
    int start = 0, end = array.length;
    while (start searched) begin = middle + 1;
    else end = middle - 1;
    }
    return -1;
    }

  97. Got my loop comparison wrong. I wrote

    while(high > low) {
    

    while I should have written

    while(high >= low) {
    
  98. Here’s mine, untested:

    	//called with 0 as lowerbound, searcharray.length - 1 as upperbound
    	//returns -1 for not found, else the location of the value
    	public int binsearch(int searcharray[],int lowerbound, int upperbound, int target) {
    		if (lowerbound > upperbound) { 
    			return -1;
    		} 
    		int search = upperbound - lowerbound / 2;
    		if (searcharray[search] < target) {
    			  return binsearch(searcharray,lowerbound,search - 1,target);)
    		} else if (searcharray[search] > target) {
    			   return binsearch(searcharray,search+1,upperbound,target);
    		}
    		return search;
    	}
    

    This is not the way I wrote it when assigned this 10 years ago in college (I’m pretty sure I had the non-recursive loop with 3 comparisons), but it is the way we were shown to do it after turning it in.
    I’ve never had to do anything like this in my career to date, for whatever reason (probably because I’m a java developer working on web based apps).

  99. Fail.
    Coded it in 10 minutes, not thinking enough, and forgot the +1/-1. Everything else is almost exactly like in the Google example. I incidentally even took care of index overflows :)

  100. Well, I’m clearly not ready to join the ranks of the great and good just yet… I got my “greater than” and “less than” the wrong way around. That amended, here it is, tested, as a Smalltalk method definition:

    binarySearchFor: target in: array range: interval

    | midIndex midValue |

    interval size = 1 ifTrue: [
    (array at: interval first) = target
    ifTrue: [^interval first]
    ifFalse: [^nil]
    ].

    midIndex := interval first + (interval size // 2).
    midValue := array at: midIndex.

    midValue = target ifTrue: [
    ^midIndex
    ].

    midValue > target ifTrue: [
    ^self binarySearchFor: target in: array range: (
    interval first to: (midIndex – 1 max: interval first)
    )
    ].

    midValue < target ifTrue: [
    ^self binarySearchFor: target in: array range: (
    (midIndex + 1 min: interval last) to: interval last
    )
    ].

  101. Not tested.


    def search(list, val, start=0, end=None):
    if not end: end = len(list)-1
    if start>end:
    return -1
    middle = (end+start) / 2
    if list[middle] == val:
    return middle
    if list[middle] val:
    return search(list, val, start=start, end=middle-1)

  102. I did manual testing by mentally stepping through my pseudocode with a few test inputs, which identified a couple of bugs that I fixed. Hope that is not cheating! After I decided that pseudocode was correct, here it is translated into Python:

    #!/usr/bin/env python
    
    def bsearch(needle, haystack):
        lower = 0
        upper = len(haystack) - 1
        idx = int(upper/2)
        found = haystack[idx] == needle
        while not found:
            if lower >= upper:
                break
            if needle > haystack[idx]:
                lower = idx + 1
            else:
                upper = idx - 1
            idx = int(.5 *(lower + upper))
            found = haystack[idx] == needle
        if found:
            return idx # found it!
        return False # indicating item not in the list
    
    if '__main__' == __name__:
        # uncomment your test input of choice
        needle = 7
        haystack = [1, 2, 3, 4, 6, 7]
        #needle = 5
        #haystack = [1, 4, 6, 9, 12]
        #needle = 4
        #haystack = [1, 4, 6, 9, 12]
        print bsearch(needle, haystack)
    
  103. Not tested:

    def search(list, val, start=0, end=None):
        if not end: end = len(list)-1
        if start>end:
            return -1
        middle = (end+start) / 2
        if list[middle] == val:
            return middle
        if list[middle] < val:
            return search(list, val, start=middle+1, end=end)
        if list[middle] > val:
            return search(list, val, start=start, end=middle-1)
    
  104. First attempt. C#. Worked in every test I could think of:


    public static int? BinSearch(IEnumerable src, int target)
    {
    if (!src.Any())
    return null;

    var n = (int)Math.Floor(src.Count() / 2d);
    var mid = src.ElementAt(n);

    if (mid == target)
    return mid;
    if (mid < target)
    return BinSearch(src.Skip(n+1), target);
    return BinSearch(src.Take(n), target);
    }

  105. You didn’t seem to provide an email to contact you with, so I’ll comment here.

    I wrote my response in Python, and there did turn out to be two bugs. One, I mixed up the label for the length of the incoming list and the label for the point to be searched, and two I forgot divide the length in half when I recursed. Ah well. Got it right in about ten minutes though, including the testing.

    def bsearch(srtd,x):
    l = len(srtd)
    if l == 0:
    return False
    med = srtd[l/2]
    print med
    if med == x:
    return True
    if x < med:
    return bsearch(srtd[:(l/2)],x)
    else:
    return bsearch(srtd[(l/2)+1:],x)

  106. I started writing as soon as I saw “Try It”, so I tested it twice before really thinking “Okay it’s done”.

    def find(value, ary)
    subarray_find(value, ary, 0, ary.length-1)
    end
    def subarray_find(value, ary, bottom, top)
    if top < bottom
    return -1
    end
    pivot_loc = ((top+bottom)/2.0).floor
    pivot_value = ary[pivot_loc]
    if pivot_value == value
    return pivot_loc
    elsif pivot_value value
    return subarray_find(value, ary, bottom, pivot_loc-1)
    end
    end

    presorted_array = [0, 1, 2, 3, 4, 5, 6, 9, 11, 25]
    presorted_array.each do |v|
    puts find(v, presorted_array) #should result in printing 0-9
    end
    puts find(17, presorted_array) #-1
    puts find(17, []) #-1

  107. Here’s my impl in Clojure. Not tested. Takes a custom search function. Is susceptible to numerical overflow, but if you have that many elements in an in-memory data structure, you have other problems.

    I’ll test it later on today if I get time. Is there a standard set of test conditions, anywhere? I’d hate to “pass” simply because I forgot an edge case in my test.

    
    (defn bsearch 
    	([data value f] (bsearch data value f 0 (count data)))
    	([data value f x y]
    		(let [idx (int (+ x (/ (- y x) 2)))
    			  test (f (nth data idx) value)]
    				(cond
    					(zero? test) idx
    					(= x idx) nil
    					(pos? test) (recur data value f idx y)
    					(neg? test) (recur data value f x idx)))))
    
    
  108. Here’s mine. I THINK it’s right, but hell if I know. No overflow bug. Using python.

    # This MAY work in python 2.6, not sure. Tested on 3.1
    ########
    # Binary searchy! Returns the index of the found element.
    def bsearch(data, toFind):
    begin = 0
    end = len(data) – 1

    while begin < (end – 1):
    pivot = int(begin + (end – begin) / 2)

    if data[pivot] == toFind:
    return pivot
    elif data[pivot] toFind:
    end = pivot

    if data[begin] == toFind:
    return begin
    elif data[end] == toFind:
    return end

    return -1

  109. in C, it took me 1 hour with all the cosmetics (randomly initializing an array or user-provided size and sorting it…). I think the core function took me about 20 minutes. Damn, so much longer than I thought.

    It seems to work at first glance. Here’s the core search function:

    int binary_search(int *array, int begin, int end, int searchvalue)
    {
    int idx = 0;

    if (end-begin == 0) return -1;
    if (end-begin == 1) {
    if (array[begin] == searchvalue) return begin;
    else return -1;
    }
    idx = begin+(end-begin)/2;
    if (array[idx] == searchvalue) return idx;

    if (searchvalue < array[idx]) return binary_search(array, begin, idx, searchvalue);
    else return binary_search(array, idx, end, searchvalue);

    }

  110. First try failed when the target was outside the range of the array. Second try was successful.

  111. Success!

    def search(array, term):
        print "searching for %d in %s" % (term, array)
        length = len(array)
        if length == 1:
            if array[0] == term:
                print "Found %s in %s." % (term, array)
                return True
            else:
                print "Did not find %s in %s." % (term, array)
                return False
        else:
            mid = length/2
            if array[mid] &lt;= term:
                return search(array[mid:], term)
            else:
                return search(array[:mid], term)
    
  112. Eh, screwed up my formatting. Here’s my successful binary search as a GitHub gist:

  113. I wrote this recursive version:

    def index_of(elements, value):
        if len(elements) == 0: return None
        index = len(elements) / 2
        if elements[index] == value: return index
        if elements[index] > value: return index_of(elements[index + 1:], value)
        return index_of(elements[:index], value)
    

    It took about 10 minutes. I expected it to be correct. I then ran this test:

    def test(elements):
        print("Not in list, below: " + str(index_of(elements, 0)))
        print("Not in list, above: " + str(index_of(elements, 20)))
        print("Finding all the elements in the list:")
        for i in xrange(len(elements)):
            print("Element %i (with value %i) was found at: %s" % \
                (i, elements[i], index_of(elements, elements[i])))
    
    list1 = [5, 6, 7, 8, 9]
    list2 = [5, 7, 12, 15]
    
    print("Test for uneven number of elements:")
    test(list1)
    
    print("Test for even number of elements:")
    test(list2)
    

    And got this result:

    Test for uneven number of elements:
    Not in list, below: None
    Not in list, above: None
    Finding all the elements in the list:
    Element 0 (with value 5) was found at: None
    Element 1 (with value 6) was found at: None
    Element 2 (with value 7) was found at: 2
    Element 3 (with value 8) was found at: None
    Element 4 (with value 9) was found at: None
    Test for even number of elements:
    Not in list, below: None
    Not in list, above: None
    Finding all the elements in the list:
    Element 0 (with value 5) was found at: None
    Element 1 (with value 7) was found at: None
    Element 2 (with value 12) was found at: 2
    Element 3 (with value 15) was found at: None
    

    Wept a little, and corrected the 2 (two!) bugs, which were extremely obvious after the fact, namely that the greater-than should be less-than and that the index returned is wrong because I forgot to add the pivot when splitting. The end result is this:

    def index_of(elements, value, offset = 0):
        if len(elements) == 0: return None
        index = len(elements) / 2
        if elements[index] == value: return index + offset
        if elements[index] < value: return index_of(elements[index + 1:], value, index + 1 + offset)
        return index_of(elements[:index], value, offset)
    
  114. Sean O'Riordan

    {-# LANGUAGE NoMonomorphismRestriction #-}
    import Control.Monad
    binsearch = ((head . filter ((1 ==) . length)) .) . iterate . listHalf
    where
    listHalf = join . (`ap` halfway) . dropTake
    dropTake needle haystack = if ((haystack !! halfway haystack) <= needle) then drop else take
    halfway = flip div 2 .length

  115. Time to flex my ruby:

    class Array
      def binary_search q
        l, h= 0, self.length-1
        until l > h
          m = l + ((h - l) / 2)
          v = self[m]
          l = m+1 if q > v
          h = m-1 if q < v
          return m if q == v
        end
        nil
      end
    end 
    
    # TESTS
    puts [1,4,394,590,1023,3924,50245].binary_search 394
    
    data = *(1..500)
    puts data.binary_search 25
    puts data.binary_search 26
    puts data.binary_search 27
    puts data.binary_search 28
    puts data.binary_search 29
    puts data.binary_search 2300
    puts data.binary_search 0
    puts data.binary_search 500
    puts data.binary_search 499
    
    alpha = %W{a b c d e f g h i j k l m n o p q r s t u v w x y z}
    puts alpha.binary_search 'f'
    puts alpha.binary_search 'b'
    puts alpha.binary_search 'bob'
    
    puts [1.3,1.4,1.5,1.6].binary_search 1.4
    

    I hope the formatting didn’t fail. Wrote once, tested (worked) then reformatted into the above for fun.

  116. It screwed with my formatting too. Here’s a fixed one:

    http://gist.github.com/371583

  117. Untested…..

    public class BinarySearch {
    	// Binary search for a target value in a given sorted array
    	// returns:
    	//    -1 if not found
    	//    index of target value if found
    	
    	public static int search(int[] array, int target) {
    		return search(array,target,0,array.length);
    	}
    	
    	// search within range, inclusive of start index, exclusive of end index
    	public static int search(int[] array, int target, int start, int end) {
    		if (start>=end) return -1;
    		
    		int middle=(start+end)/2;
    		
    		int middleValue=array[middle];
    		
    		if (target==middleValue) {
    			// we have found it!
    			return middle;
    		} else if (target<middleValue) {
    			// search bottom part
    			return search(array,target,start,middle);
    		} else {
    			// search top part
    			return search(array,target,middle+1,end);
    		}
    	}
    	
    	public static void main(String[] args) {
    		if (search(new int[] {1,2,3,5},   3) != 2  ) throw new Error("Whoops!");
    		if (search(new int[] {1,2,3,5},   9) != -1 ) throw new Error("Whoops!");
    		if (search(new int[] {1,2,3,5}, -10) != -1 ) throw new Error("Whoops!");
    		if (search(new int[] {1,2,3,5}, 4  ) != -1 ) throw new Error("Whoops!");
    		if (search(new int[] {1,2,3,5}, 1  ) != 0  ) throw new Error("Whoops!");		
    		if (search(new int[] {},        1  ) != -1 ) throw new Error("Whoops!");		
    	}
    }
    
  118. Wrote this and thought through some test cases on paper. Took about 40 min, over which time I also pared it down from about 2x as long. Tested using arrays of 0,1,2,3 elements with search items hitting each as well as missing below and between each.
    [pre]
    def binarySearch(A, t):
    a, b = 0, len(A)-1
    while b >= a:
    mp = a + (b-a)/2
    if A[mp] == t:
    return t
    elif A[mp] < t:
    a = mp+1
    else:
    b = mp-1
    return None
    [/pre]

  119. Woohoo! I did it!

    Of course, that assumes I implemented the testing algorithm correctly as well…

    public static void main(String[] args) {
    		for (int i = 0; i < 10000; i++){
    			int[] testArray = new int[(int) Math.random() * 10];
    			for (int k = 0; k < testArray.length; k++)
    				testArray[k] = (int) Math.random() * 10;
    			java.util.Arrays.sort(testArray);
    			int testSearch = (int) Math.random() * 100;
    			int result = bSearch (testArray, testSearch);
    			if (result == -1)
    				System.out.println (java.util.Arrays.binarySearch(testArray, testSearch) < 0);
    			else
    				System.out.println (java.util.Arrays.binarySearch(testArray, testSearch) == result);
    		}
    
    	}
    	
    	public static int bSearch (int[] sortedArray, int value){
    		int minRange = 0, maxRange = sortedArray.length - 1;
    		while (maxRange - minRange > 0){
    			int index = minRange + (maxRange - minRange) / 2;
    			if (sortedArray[index] > value)
    				maxRange = index - 1;
    			else if (sortedArray[index] < value)
    				maxRange = index + 1;
    			else
    				return index;
    		}
    		return -1;	
    	}
    
  120. I failed

  121. Below is the first version I came up with that I think will work. I’m going to try my luck and not test it :) I know this could be simplified further, but for science I’ll leave it like this.

    function binsearch(needle, arr) {
        var start = 0;
        var end = arr.length - 1;
        do {
            var midpoint = Math.floor((start + end) / 2);
            if (arr[midpoint] == needle) {
                return midpoint;
            }
            if (arr[midpoint] < needle) {
                start = midpoint + 1;
            } else {
                end = midpoint - 1;
            }
        } while (start < end);
        if (start == end && arr[start] == needle) {
            return midpoint;
        }
        return -1;
    }
    
  122. # vim:tabstop=8:shiftwidth=4:smarttab:expandtab:softtabstop=4:autoindent:
    # Python 2.5.4
    # Non-obvious ends of blocks have been indicated with comments, in
    # case the indents get lost when posting as a comment on the blog.
    # This code has only been syntax checked.

    class BinarySearch:
    def __init__(self):
    ”’ Initialize ”’

    def search(self, num, list):
    list_length = len(list)
    lower_bound = 0
    upper_bound = list_length – 1
    found_position = -1
    done = False
    while not done:
    if upper_bound == lower_bound:
    done = True
    if num == list[upper_bound]:
    found_position = upper_bound
    #fi
    else:
    middle_point = lower_bound + int((upper_bound + 1 – lower_bound) / 2)
    if num == list[middle_point]:
    found_position = middle_point
    done = True
    elif num > list[middle_point]:
    lower_bound = middle_point
    if lower_bound lower_bound:
    upper_bound -= 1
    #fi
    #fi
    #fi
    #elihw
    return found_position
    #fed

    if __name__ == ‘__main__’:
    bs = BinarySearch()
    for list_length in range(1, 10):
    list = range(0, list_length)
    for num in list:
    pass
    #print num, list, bs.search(num, list)
    #rof
    #rof

  123. pete gamache

    Hell, if we’re posting without testing, I might as well go big.

    
          INTEGER FUNCTION IBSRCH(ELT, ARR, IMIN, IMAX)
          INTEGER ELT, IMIN, IMAX, ARR
          DIMENSION ARR(IMIN:IMAX)
          IBSRCH = -1
    100   IMID = IMIN + (IMAX-IMIN)/2
          IF (ELT-ARR(IMID)) 110, 120, 130
    110   IF (IMID.EQ.IMAX) GOTO 199
          IMAX = IMID 
          GOTO 100
    120   IBSRCH = IMID
          GOTO 199
    130   IF (IMID.EQ.IMIN) GOTO 199
          IMIN = IMID
          GOTO 100
    199   RETURN
          END
    
  124. def bsearch(value,array):
        if array == []:
            return False
        first = 0
        last = len(array)-1
        middle = (last-first)/2
        while first + 1 < last:
            if array[middle] > value:
                last = middle
            else:
                first = middle
            middle = (last-first+1)/2+first
            if array[middle] == value:
                return True
        return array[first] == value or array[last]==value
    

    works (as far as i can tell with some quick testing)

  125. Matthias Goergens

    In Haskell

    module Main where
    import Data.Sequence as Se
    import Test.QuickCheck
    import Data.Generics.Aliases (orElse)
    import Data.Function
    
    
    bin :: (Ord key) => key -> Se.Seq (key, item) -> Maybe item
    bin key s | Se.null s = Nothing
              | key < fst (Se.index s 0) || key > fst (Se.index s (l-1)) = Nothing
              | l == 1 = Just . snd . Se.index s $ 0
              | otherwise = uncurry (on orElse (bin key)) $ Se.splitAt (l `div` 2) s
        where l = Se.length s
    
  126. int bsearch(int array[], int T) {
            int lowRange = 0, highRange = sizeof(array)/4;
    
            while (42) {
                    int testID = (highRange - lowRange)/2;
                    int test = array[testID];
                    if (test == T)
                            return testID;
                    if (test < T)
                            lowRange = testID;
                    if (test > T)
                            highRange = testID;
            }
            return -1;
    }
    
  127. Next, describe an algorithm to implement an insertion sort where you insert data into the array, in the correct position so that the array doesn’t need to be sorted before use. Finally, alter that algorithm for dealing with inserting already sorted data into the array. As someone once said, been there, done that… :-)

    In any case, these algorithms appear simple on the face of it, but implementing them correctly can take a considerable of time and doh!

    BTW, you only mentioned not cheating by use of bsearch() – what about qsort()? :-)

  128. Never mind! This a search, not a sorting problem… (Sound of Homer Simpson slapping his forhead) Doh!

  129. Stephen Veit

    Failed before testing:

    def search(array, value, bottom = 0, top = nil)
    top ||= (array.length – 1)

    middle = (top + bottom) / 2
    if array[middle] == value
    return middle
    elsif top == bottom
    return nil
    elsif value < array[middle]
    return search(array, value, bottom, middle)
    else
    return search(array, value, middle, top)
    end
    end

    After testing:

    def search(array, value, bottom = 0, top = nil)
    top ||= (array.length – 1)

    middle = (top + bottom) / 2
    if array[middle] == value
    return middle
    elsif top <= bottom
    return nil
    elsif value < array[middle]
    return search(array, value, bottom, middle – 1)
    else
    return search(array, value, middle + 1, top)
    end
    end

  130. Failed. Wrote a recursive implementation that returned the offset:

    def bsearch(a, k):
        """Return the position i in sorted a such that a[i] == k, or -1 if
        no such i exists.  If a is not sorted, result is not defined"""
    

    …but didn’t track the offset of splits, so it failed as soon as it recursed.

  131. Seems to work correctly on the first try. Having looked through the comments, it appears my code is virtually identical to the stuff by Lawrence Kesteloot, except in a different language. Crazy.

    # Return index of value, nil if not found.
    def bsearch(a, value)
      low = 0
      high = a.length - 1
      
      while high &gt;= low
        mid = low + ((high - low) / 2)
        if a[mid]  value
          high = mid - 1
        else
          return mid
        end
      end
      
      return nil
    end
    
  132. So with no testing I was 95% there…i think.

    But who writes anything without testing to find there errors? It’s an unreasonable expectation to be able to write anything more than the most trivial program without having errors prior to testing.

  133. Here’s mine. I don’t know if this is good Lua style because I don’t know Lua very well.

    It could be complained that perhaps the routine ought to return the index at which the value would need to be inserted, rather than nil, on not-found. I didn’t think of that until after I tested it.

    #!/usr/bin/lua
    -- https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    
    -- Apparently I am one of the 10%, because this code worked the first
    -- time I tested it. It took me nine minutes to write and test.
    -- > print(loadfile("bsearch.lua"))
    -- function: 0x8865890
    -- > loadfile("bsearch.lua")()
    -- > print(bsearch({3, 5, 8, 11}, 8))
    -- 3
    -- > print(bsearch({3, 5, 8, 11}, 5))
    -- 2
    -- > print(bsearch({3, 5, 8, 11}, 3))
    -- 1
    -- > print(bsearch({3, 5, 8, 11}, 11))
    -- 4
    -- > print(bsearch({3, 5, 8, 11}, 12))
    -- nil
    -- > print(bsearch({3, 5, 8, 11}, 10))
    -- nil
    -- > print(bsearch({3, 5, 8, 11}, 1))
    -- nil
    
    -- I think this is a good task to use to teach thinking in terms of
    -- formal logic.
    
    -- find index of thing in sorted things in the range [first, last)
    function bsearch_2(things, thing, first, last)
       if first == last then return nil end
       assert(last > first)
    
       local midpoint = math.floor((first + last) / 2)
       assert(midpoint >= first)
       assert(midpoint < last)
    
       local test = things[midpoint]
       if test == thing then return midpoint end
       if test > thing 
       then return bsearch_2(things, thing, first, midpoint)
       else return bsearch_2(things, thing, midpoint+1, last)
       end
    end
    
    function bsearch(things, thing) 
       return bsearch_2(things, thing, 1, #things+1) 
    end
    

    It’s not my first time, but it’s been a few years.

    After writing this I looked at some of the comments. One that I thought was particularly interesting was @Juanjo’s, which I think will infinite-loop if you look for, say, 3 in the one-element array [2]. Haven’t tested that, though.

  134. Paul Hubbard

    In python, as yet untested:
    {source}

    def bsearch(search_value, input_array):
    “””
    Assume array is sorted, min at [0] and max at [-1]
    “””

    # Ending conditions
    if len(input_array) <= 0:
    return False
    if search_value input_array[-1]:
    return False

    split_index = int(math.floor(len(input_array) / 2))

    if input_array[split_index] == search_value:
    return True

    if search_value > input_array[split_index]:
    return bsearch(search_value, input_array[split_index+1:])
    else:
    return bsearch(search_value, input_array[0:split_index])
    {/source}
    Off to write unit tests and see….

  135. Does the "pre" tag work in comments?
    
  136. Paul Hubbard

    Ooops… RTFM fail.

    
    def bsearch(search_value, input_array):
        """
        Assume array is sorted, min at [0] and max at [-1]
        """
    
        # Ending conditions
        if len(input_array) <= 0:
            return False
        if search_value < input_array[0]:
            return False
        if search_value > input_array[-1]:
            return False
    
        split_index = int(math.floor(len(input_array) / 2))
    
        if input_array[split_index] == search_value:
            return True
        
        if search_value > input_array[split_index]:
            return bsearch(search_value, input_array[split_index+1:])
        else:
            return bsearch(search_value, input_array[0:split_index])
    
  137. Tried it in Python and failed because of a typo. Once I corrected the typo, the rest was correct.

  138. far from perfect. 15 minutes. doesn’t work when item is not in array (stack overflow)

    class Array
      def bsearch(n, from = 0, to = self.length)
        middle = from + ((to - from) / 2)
        value = self[middle]
    
        if value == n
          return middle
        elsif value < n
          return bsearch(n, middle, to)
        else
          return bsearch(n, from, middle)
        end
      end
    end
    
    arr = [1,2,3,4,5,6,7,8,9,10]
    
    arr.each do |search|
      index = arr.bsearch(search)
      puts "search(#{search}) => #{index}, #{arr[index]}"
    end
    
  139. Yup when in doubt I reach for my Perl hammer. It’s sad really. It’s worked for all the tests I’ve run but I’m sure I’m missing something.

    sub binsearch {
    	my $target = shift;
    	my @range = @_;
    
    	my $found = 0;
    	while ( @range ) {
    		my $idx = floor( @range / 2 );
    		my $mid = $range[$idx];
    		if ( $mid == $target ) {
    			$found = 1;
    			last;
    		} elsif ( $mid < $target ) {
    			@range = @range[$idx+1..$#range];
    		} else {
    			@range = @range[0..$idx-1];
    		}
    	}
    
    	return $found;
    }
    
  140. Ruby solution – Technically a fail due to if/else syntax error (ruby’s still a new language), but my algorithm implementation was sound…

    Returns the array index of the element, if found, or -1 if not. (This seemed the only sensible return value to me… why return a number you already know?)

    def real_binary_sort(target,i_start,i_end,sorted_array)
      @l = i_end - i_start
      if (@l<0)
        return "-1" 
      end
      @index = i_start + (@l%2==0 ? @l/2 : (@l+1)/2)
      if (target == sorted_array[@index])
        return @index
      elsif (target > sorted_array[@index])
        return real_binary_sort(target,@index+1,i_end,sorted_array)
      else 
        return real_binary_sort(target,i_start,@index-1,sorted_array)
      end
    end
    
    def binary_sort(target,sorted_array)       
      rec_binary_sort(target,0,sorted_array.length-1,sorted_array)
    end
    
  141. Here’s my attempt in Clojure. I wrote it and subsequently tested it. I think it’s working. I may be wrong.

    (defn bsearch
      ([a e]
        (bsearch a e 0 (dec (count a))))
      ([a e l h]
        (if (not (< h l))
          (let [m (floor (/ (+ l h) 2))
                o (a m)]
            (cond (> e o) (recur a e (inc m) h)
                  (< e o) (recur a e l (dec m))
                  (= e o) m)))))
    
  142. Here’s a recursive pseudocode. Looks like I almost wrote in Python.

    bsearch (T element, lst) returns index of element if in sorted list lst, otherwise throws. Assumes element has operator

    if element is null: throw NullElement // assuming comparison operator can’t be defined for null
    if lst is null: throw NullList
    int len = sorted_list.length
    if len == 0: throw EmptyList
    int n = len / 2
    T curr = sorted_list[n]
    // need to bottom out!
    if curr == element:
    return n
    else if len == 1:
    throw ElementNotFound
    else if len == 2 && curr < element:
    throw ElementNotFound
    else if curr element: // search down
    return bsearch(element, lst[(0)..(n-1)])

  143. Steve Witham: yes, the <pre> tag does sort of work in WordPress comments, but it won’t do all the things you’d want it to do; in particular, it throws away indentation, which is pretty critical in code samples (especially if you write Python). Instead, use {source}…{/source}, but use square brackets instead of curlies.

  144. Nabeel Ali Memon

    public static int binarySearch(int[] array, int begin, int end, int value) {
    if (begin array.length – 1) throw new ArrayIndexOutOfBoundsException();
    int high = end;
    int low = begin;
    int mid = low + ((high – low) / 2);

    if (value == array[mid]) return mid;
    if (value > array[mid]) return binarySearch(array, mid + 1, high, value);
    if (value < array[mid]) return binarySearch(array, low, mid – 1, value);

    return -1;
    }

    public static int binarySearch(int[] array, int value) {
    return binarySearch(array, 0, array.length – 1, value);
    }

  145. Here’s my first pass in ruby I think it works

    def bsearchexplicit(arr, firstindex, lastindex, n) 
       return (n == arr[firstindex]) || (n == arr[lastindex]) if (lastindex - firstindex < 2 ) 
    
    
      middleindex = ((firstindex + lastindex)/2).floor
      middlenum = arr[middleindex]
      
      return true if (n == middlenum) 
      return bsearchexplicit(arr, firstindex, middleindex, n) if (n < middlenum)
       
      return bsearchexplicit(arr, middleindex, lastindex, n)
    end
    
    def bsearch(arr, n) 
      return bsearchexplicit(arr, 0, arr.length - 1, n)
    end
    
  146. I failed.
    Wrote it in Ruby, ran it. Buggy.

    Here’s the version I ended up getting working.
    http://pastebin.com/rAwy1WcJ

  147. Update after testing and seeing the blog update about source formatting.

    # vim:tabstop=8:shiftwidth=4:smarttab:expandtab:softtabstop=4:autoindent:
    # Python 2.5.4
    # Non-obvious ends of blocks have been indicated with comments, in
    # case the indents get lost when posting as a comment on the blog.
    
    class BinarySearch:
        def __init__(self):
            ''' Initialize '''
    
        def search(self, num, list):
            list_length = len(list)
            lower_bound = 0
            upper_bound = list_length - 1
            found_position = -1
            done = False
            while not done:
                if upper_bound == lower_bound:
                    done = True
                    if num == list[upper_bound]:
                        found_position = upper_bound
                    #fi
                else:
                    middle_point = lower_bound + int((upper_bound + 1 - lower_bound) / 2)
                    if num == list[middle_point]:
                        found_position = middle_point
                        done = True
                    elif num > list[middle_point]:
                        lower_bound = middle_point
                        if lower_bound < upper_bound:
                            lower_bound += 1
                        #fi
                    else:
                        upper_bound = middle_point
                        if upper_bound > lower_bound:
                            upper_bound -= 1
                        #fi
                    #fi
                #fi
            #elihw
            return found_position
        #fed
    
    if __name__ == '__main__':
        bs = BinarySearch()
        for list_length in range(1, 10):
            list = range(0, list_length)
            for num in list:
                print num, list, bs.search(num, list)
            #rof
        #rof
    
  148. Argh, looks like my paste failed to get my search up case. Not sure why.

    bsearch (T element, lst) returns index of element if in sorted list lst, otherwise throws. Assumes element has operator
    
    if element is null throw NullElement // assuming comparison operator can't be defined for null
    if lst is null throw NullList
    int len = sorted_list.length
    if len == 0 throw EmptyList
    int n = len / 2
    T curr = sorted_list[n]
    // need to bottom out!
    if curr == element
      return n
    else if len == 1
      throw ElementNotFound
    else if len == 2 && curr < element
      throw ElementNotFound
    else if curr  element // search down
      return bsearch(element, lst[(0)..(n-1)])
    
  149. Nabeel Ali Memon

    Josh Bloch has already discussed Binary Search in connection with JDK and Programming Pearls in a great detail here http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html

    [Mike says: yes, Nabeel. You’d have thought I’d have linked to that article, wouldn’t you? Oh! Wait! …]

  150. Doh! Of course I missed the point about returning the index of item in the array (which the description from the book doesn’t really point out). After a bit of thought here was my updated version to do that.

    sub binsearch {
    	my $target = shift;
    	my @range = @_;
    
    	my $found = -1;
    	my $delta = 0;
    	while ( @range ) {
    		my $idx = floor( @range / 2 );
    		my $mid = $range[$idx];
    		if ( $mid == $target ) {
    			$found = $delta + $idx;
    			last;
    		} elsif ( $mid < $target ) {
    			@range = @range[$idx+1..$#range];
    			$delta += $idx+1;
    		} else {
    			@range = @range[0..$idx-1];
    		}
    	}
    
    	return $found;
    }
    
  151. Third and final try, lt/gt seems to be the problem. Incidentally “preview comment” would have been nice.

    bsearch (T element, lst) returns index of element if in sorted list lst, otherwise throws. Assumes element has operator< == >
    
    if element is null throw NullElement // assuming comparison operator can't be defined for null
    if lst is null throw NullList
    int len = sorted_list.length
    if len == 0 throw EmptyList
    int n = len / 2
    T curr = sorted_list[n]
    // need to bottom out!
    if curr == element
      return n
    else if len == 1
      throw ElementNotFound
    else if len == 2 && curr < element
      throw ElementNotFound
    else if curr < element // search up
      return n + bsearch(element, lst[(n+1)..(len-1)]) // works if lst[2..1] returns empty
    else if curr > element // search down
      return bsearch(element, lst[(0)..(n-1)])
    
  152. Yay, passed with this C:

    int *bsearch(int *base, int size, int key)
    {
      if(size <= 0 ) {
        return 0;
      }
    
      int halfsize = size >> 1;
      int pivot = base[halfsize];
      
      if(pivot == key) {
        return base + halfsize;
      } 
    
      if(pivot < key) {
        return bsearch(base + halfsize + 1, size - (halfsize + 1), key);
      }
    
      return bsearch(base, halfsize, key);
    }
    
  153. Iterative python, seems like a lot of people did the same. Once I remembered how the range of an array is specified, think it is OK. Caught the index out of bounds condition that others seemed to miss. Wouldn’t take a bet that something else is missing though!

    def bshalf(array, N):
    if len(array)==1:
    if N==array[0]:
    print ‘found’, N
    return 1
    else:
    print ‘not in array, in bounds’
    return 0
    halflenarr=int(len(array)/2)
    print halflenarr, len(array), array
    if N>array[halflenarr]:
    return bshalf(array[halflenarr+2: len(array)], N)
    else:
    return bshalf(array[0: halflenarr+2], N)

    def bs(array, N):
    lenarr=len(array)
    if N>array[lenarr-1]:
    print ‘outside initial array’
    return 0
    return bshalf(array, N)

  154. Javascript, iterative, seems to work.

    // returns index of value, or -1 if not found. array must contain sorted numbers.
    
    function binSearch(array, value) {
      var lo = 0;
      var hi = array.length - 1;
      var mid;
    
      while(hi > lo) {
        mid = Math.floor((hi + lo) / 2);
        
        if(array[mid] < value) {
          lo = mid + 1;
        } else {
          hi = mid;
        }
      }
    
      return(array[lo] === value ? lo : -1);
    }
    
  155. I did right before testing it, but it was a bit harder than I thought it should be and wasn’t the most elegant code.

  156. #tail recursive binary search in python. Returns -1 or index of item in array
    def bsearch(arr, value, lidx=None, hidx=None):
        if arr is None:
        	return -1
    
    
        if lidx==None:
            lidx=0
            hidx=len(arr)
    
    
        #get the middle index and middle value in the section of the array    
        midx = (hidx+lidx)/2
        midval = arr[midx]
    
    
        # see if we found it
        if midval  == value:
        	return midx
    
        #if we didn't find it, and there is nothing else to search, return not found
        elif hidx == lidx:
            return -1
        
        # if we found it and it is greater, look in the top half
        elif value > midval:
            return bsearch(arr,value,midx+1,hidx)
    
        # value < midval, look in the bottom half
        else :
            return bsearch(arr,value,lidx,midx)
         
         
    

    Forgot a “:” on else, otherwise right.

  157. Cool Post!
    Almost correct in python on first try…. It would return the correct index, and would handle out-of-bounds conditions, but with a search value in the range of but not in the list I hit a loop. I fixed it on the 2nd try. You can decide whether to count it as a success or fail.

  158. Eddie Hedges

    // c# implementation of binary search

    public int BinarySearch( int[] array, int index)
    {
    int start = 0;
    int end = array.Length;

    while(start < end)
    {
    int mid = (start + end)/2;

    if(index < array[mid])
    {
    end = mid;
    }
    else start = mid + 1;
    }
    return start;
    // you could also return end because theoretically when the loop ends start and end will be the same spot in the arrray :)
    }

  159. Paul Hubbard

    Code and unittests (all pass!) at http://pastebin.com/Rjr6wx1b

  160. ruby-esque pseudocode, untested:

    bsearch(arr,val){
    if(arr.length==0)
    return nil;
    else if(arr.length==1 and val!=arr[0])
    return nil;
    n = (arr.length-1)/2
    if(arr[n]==val)
    return n
    else if(arr[n]>val)
    return bsearch(arr[0:n-1],val)
    else
    return bsearch(arr[n+1:arr.length-1],val)
    }

  161. William Laffin

    Success!!! took me 40 minutes.

    def bsearch(list,elem,key=None):
        if key is None:
            key = lambda x:x
        if len(list) &gt; 0:
            return bsearchr(list,elem,0,len(list)-1,key)
        return -1
    
    
    def bsearchr(list, elem, i, j, key):
        if (j - i) &lt; 2:
            if key(list[i]) == elem:
                return i
            if key(list[j]) == elem:
                return j
            return -1
        else:
            mindex = (j - i)/2 + i
            if key(list[mindex]) &lt; elem:
                return bsearchr(list, elem, mindex, j, key)
            else:
                return bsearchr(list, elem, i, mindex, key)
    
  162. Whipped this up in Obj-C. Haven’t even compiled it, much less tested it. But I believe it should work (shouldn’t even have the integer overflow bug documented in that link, and yes I wrote the code before following the link).

    @interface NSArray (BinarySearch)
    - (NSUInteger)indexOfObjectUsingBinarySearch:(id)obj;
    @end
    
    @implementation NSArray (BinarySearch)
    // performs a binary search in the array, returning the index of the found object or NSNotFound
    // this assumes the array is in sorted order according to the compare: selector
    - (NSUInteger)indexOfObjectUsingBinarySearch:(id)obj {
        NSRange range = NSMakeRange(0, [self count]);
        while (range.length > 0) {
            NSUInteger pivotIdx = range.location + (range.length / 2); // center idx, rounded down
            id pivot = [self objectAtIndex:pivotIdx];
            switch ([obj compare:pivot]) {
                case NSOrderedSame:
                    // we've found the object
                    return pivotIdx;
                case NSOrderedAscending:
                    // object is in the lower half
                    range.length = pivotIdx - range.location;
                    break;
                case NSOrderedDescending:
                    // object is in the upper half
                    range = NSMakeRange(pivotIdx+1, NSMaxRange(range) - (pivotIdx+1));
                    break;
            }
        }
        return NSNotFound;
    }
    @end
    
  163. Updated (and simpler) test section for my implementation, taking into account the need (mentioned in other comments) to test for numbers outside the range of values in the list and to test for numbers that are in the range, but not in the list:

    if __name__ == '__main__':
        bs = BinarySearch()
        list_length = 10
        list = [1, 3, 5, 7, 9]
        for num in range(0, 11):
            print num, list, bs.search(num, list)
        #rof
    
  164. Pingback: Am I one of the 10% of programmers who can write a binary search? « Michael Conigliaro

  165. i posted my solution here: http://conigliaro.org/2010/04/19/am-i-one-of-the-10-of-programmers-who-can-write-a-binary-search/

    be gentle. i haven’t been a full time programmer in quite a few years. ;-)

  166. I gave this a go, and did some preliminary testing. It appears to function properly. Had to brush back up on my c skillz though.

    /*  This program will find the specified number in a pre-sorted array using
    	a binary search algorithm 
    	
    	command line: bsearch    ...  */
    
    #define DEBUG	1
    
    #include 	
    #include 
    
    int main(int argc, char *argv[]) {
    	
    	// Declare and Initialize our Variables
    	int search_value = 0;
    	int *sorted_array;
    	int range_lo = 0;
    	int range_hi = (argc - 3);
    	int array_center = 0;
    	
    	// For returning info
    	int algo_iterations = 0;
    	int found_index = -1;
    	
    	// Generic Counter
    	int i = 0;
    	
    	// If we do not have at least three values passed, then the command line syntax is wrong
    	if (argc &lt; 3) {
    		printf(&quot;\nUsage:\nbsearch    ... \n\n");
    		return 0;
    	}
    	
    	// Malloc memory for incoming array and make sure we were successful
    	sorted_array = (int*) malloc(sizeof(int) * (argc - 2));
    	if (sorted_array == NULL) {
    		printf("\nUnable to allocate memory for sorted array!\n\n");
    		return 1;
    	}
    	
    	// Get our search value
    	search_value = atoi(argv[1]);
    	
    	// Create our destination array
    	for (i = 0; i  1) {
    			array_center = ((range_hi - range_lo) / 2) + range_lo;
    		}
    		else if ((range_hi - range_lo) == 1) {
    			array_center = range_lo;
    		}
    		
    		#ifdef DEBUG
    		printf("\n\nLoop #%d:\nrange_lo == %d\nrange_hi == %d\narray_center == %d\n\n", algo_iterations, range_lo, range_hi, array_center);
    		#endif
    		
    		// Divide and Conquer our array
    		
    		// See if we can short circuit
    		if (sorted_array[array_center] == search_value) {
    			found_index = array_center;
    		}
    		else if (sorted_array[array_center] &lt; search_value) {		// The search value falls in the top half
    			if ((range_hi - range_lo) == 1) {
    				range_lo = range_hi;
    			}
    			else {
    				range_lo = array_center;
    			}
    		}
    		else {
    			range_hi = array_center;
    		}
    	}
    	
    	switch (found_index) {
    		case -1:
    		case -2:
    			printf(&quot;\n\nNeedle %d not found in haystack!\n\n&quot;, search_value);
    			free(sorted_array);
    			return 0;
    			break;
    		default:
    			printf(&quot;\n\nNeedle %d found at index position %d, using %d iterations.\n\n&quot;, search_value, found_index, algo_iterations);
    			free(sorted_array);
    			return 0;
    			break;
    	}
    }
    
    
  167. Sorry, let me try that again:

    /*  This program will find the specified number in a pre-sorted array using
    	a binary search algorithm 
    	
    	command line: bsearch <num_to_find> <array_index_0> <array_index_1> ... <array_index_n> */
    
    #define DEBUG	1
    
    #include <stdlib.h>	
    #include <stdio.h>
    
    int main(int argc, char *argv[]) {
    	
    	// Declare and Initialize our Variables
    	int search_value = 0;
    	int *sorted_array;
    	int range_lo = 0;
    	int range_hi = (argc - 3);
    	int array_center = 0;
    	
    	// For returning info
    	int algo_iterations = 0;
    	int found_index = -1;
    	
    	// Generic Counter
    	int i = 0;
    	
    	// If we do not have at least three values passed, then the command line syntax is wrong
    	if (argc < 3) {
    		printf("\nUsage:\nbsearch <num_to_find> <array_index_0> <array_index_1> ... <array_index_n>\n\n");
    		return 0;
    	}
    	
    	// Malloc memory for incoming array and make sure we were successful
    	sorted_array = (int*) malloc(sizeof(int) * (argc - 2));
    	if (sorted_array == NULL) {
    		printf("\nUnable to allocate memory for sorted array!\n\n");
    		return 1;
    	}
    	
    	// Get our search value
    	search_value = atoi(argv[1]);
    	
    	// Create our destination array
    	for (i = 0; i < (argc - 2); i++) {
    		sorted_array[i] = atoi(argv[i + 2]);
    	}
    	
    	// Iterate through our array until we find the result, or find nothing
    	while (found_index == -1) {
    		// Increment our iteration counter
    		algo_iterations++;
    		
    		// Terminating condition
    		if (range_hi == range_lo) {
    			if (sorted_array[range_hi] == search_value) {
    				found_index = range_hi;
    			}
    			else {
    				found_index = -2;	// Search int not found
    			}
    		}
    		
    		// Find the center of our array
    		if ((range_hi - range_lo) > 1) {
    			array_center = ((range_hi - range_lo) / 2) + range_lo;
    		}
    		else if ((range_hi - range_lo) == 1) {
    			array_center = range_lo;
    		}
    		
    		#ifdef DEBUG
    		printf("\n\nLoop #%d:\nrange_lo == %d\nrange_hi == %d\narray_center == %d\n\n", algo_iterations, range_lo, range_hi, array_center);
    		#endif
    		
    		// Divide and Conquer our array
    		
    		// See if we can short circuit
    		if (sorted_array[array_center] == search_value) {
    			found_index = array_center;
    		}
    		else if (sorted_array[array_center] < search_value) {		// The search value falls in the top half
    			if ((range_hi - range_lo) == 1) {
    				range_lo = range_hi;
    			}
    			else {
    				range_lo = array_center;
    			}
    		}
    		else {
    			range_hi = array_center;
    		}
    	}
    	
    	switch (found_index) {
    		case -1:
    		case -2:
    			printf("\n\nNeedle %d not found in haystack!\n\n", search_value);
    			free(sorted_array);
    			return 0;
    			break;
    		default:
    			printf("\n\nNeedle %d found at index position %d, using %d iterations.\n\n", search_value, found_index, algo_iterations);
    			free(sorted_array);
    			return 0;
    			break;
    	}
    }
    
  168. Jeshua Smith

    I was confident in my code until I was about to write the first testcase and realized that I’d forgotten to test for an array size of zero, but otherwise bugfree. Unlike most of the code posted here, I tested the target value against the first and last entry before doing a divide and conquer loop, since the definition states that the range being tested is the range in which the value must lie.

  169. it’s ruby, not tested. not elegant, but it “should” work.

    def bin_search_recur(arr, r_begin, r_end, to_find)
      if(r_begin == r_end)
        if(arr[r_begin] == to_find)
          return r_begin
        else
          return -1
        end
      else
        pivot = r_begin + (r_end - r_begin) / 2
        if(arr[pivot] == to_find)
          return pivot
        elsif(arr[pivot] > to_find)
          return bin_search_recur(arr, r_begin, pivot - 1, to_find)
        else
          return bin_search_recur(arr, pivot + 1, r_end, to_find)
        end
      end
    end
    
    def bin_search(arr, to_find)
      return bin_search_recur(arr, 0, arr.size - 1, to_find)
    end
    
  170. Failed and took forever – now I hate my life — even more.

  171. Here’s my go. Took me about 20 minutes. I guess I technically failed as it didn’t work on my first test, but it was really just a typo (I had first/last reverse on line 13). After fixing that, it seems to work fine:


    # Taking on the Binary Search challenge
    # https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    class Array
    # Binary search; assumes array is sorted
    # If value is found in the array, it returns an index where it can be found.
    # If the value occurs multiple times in the array, it will just return the
    # first place it is found (not necessarily the first occurrence in the array).
    # If the value is not found, it returns nil.
    def bsearch(value)
    return nil if self.length == 0
    range = (0..self.length1)
    while range.last >= range.first
    mid = (range.lastrange.first) / 2 + range.first
    case self.at(mid) <=> value
    when 0 # values are equal
    return mid
    when 1 # mid is greater
    range = (range.first..mid1)
    when1 # value is greater
    range = (mid+1..range.last)
    end
    end
    return nil
    end
    end

    view raw

    bsearch.rb

    hosted with ❤ by GitHub

    It would be helpful if you had a test set of searches that could be used to verify correctness.

  172. Dietrich Epp

    I know it’s right, I don’t *need* to test it.

    struct entry {
        char *key;
        char *value;
    };
    
    char *lookup(struct entry *table, size_t size, char const *key)
    {
        size_t l = 0, r = size, m;
        int c;
        while (l < r) {
            m = (l + r) / 2;
            c = strcmp(key, table[m].key);
            if (c < 0)
                r = m;
            else if (c > 0)
                l = m + 1;
            else
                return table[m].value;
        }
        return NULL;
    }
    
  173. Found a bug after adding the following two test conditions:

        num = 5
        list = []
        print num, list, bs.search(num, list)
        list = None
        print num, list, bs.search(num, list)
    

    Fails on an empty list:

    5 []
    Traceback (most recent call last):
      File "C:\svnwork\circulation\utility\binary-search.py", line 52, in <module>
        print num, list, bs.search(num, list)
      File "C:\svnwork\circulation\utility\binary-search.py", line 24, in search
        if num == list[middle_point]:
    IndexError: list index out of range
    

    Commented out the first of the two new tests and the second new test also fails:

    5 None
    Traceback (most recent call last):
      File "C:\svnwork\circulation\utility\binary-search.py", line 54, in <module>
        print num, list, bs.search(num, list)
      File "C:\svnwork\circulation\utility\binary-search.py", line 11, in search
        list_length = len(list)
    TypeError: object of type 'NoneType' has no len()
    
  174. Works for all my test cases.

    Don’t flame me for using java.

    public static int Bsearch(int num, int[] array, int start, int end) {
    		
    		
    		int index = start + (end - start)/2;
    		if (num == array[index])
    			return index;
    		
    		else {
    			if (start == end)
    				return -1;
    		
    			if (num &gt; array[index])
    			return Bsearch(num, array, index+1, end);
    		
    			else return Bsearch(num, array, start, index-1);
    		}		
    	}
    	
    	public static int Bsearch(int num, int[] array) {
    		return Bsearch(num, array, 0, array.length-1);
    	}
    
  175. LOL @ Emo

    … who I assume is joking …

    :-)

    Brad and others have expressed a wish that I’d provided a set of testcases. Unfortunately, to be useful such a set would need to be made available in a language usable by the code being tested, and as at least a dozen different languages have been used that’s not really feasible. Maybe I should have picked a lowest-common-denominator language like Java and mandated that … but I am glad I didn’t. It’s done my heart good to see the range of languages used here.

  176. Untested

    #include <cstdlib>
    // first: pointer to the first element
    // last: pointer to one past the last element
    // value: value to find
    // returns NULL if not found, pointer to the element if found.
    template <typename T>
    T *bs(T *first, T *last, const T &value)
    {
        if (first == last) return NULL;
        T *middle = (first+last)/2;
        if (value < *middle) return bs(first, middle, value);
        if (value > *middle) return bs(middle+1, last, value);
        return value == *middle ? middle : NULL;
    }
    
  177. I fail, after I submitted I realized I didnt account for the empty array case. The rest was correct

  178. Added two lines to the top of the search method:

        def search(self, num, list):
            if not list:
                return -1
    

    All of my tests pass now.

  179. Tested, but no corrections seemed necessary. Of course, I iterated through the three examples at the bottom of my code by hand. Not my prettiest code, but it handles the major cases. Took me about 35 minutes, including testing. The code was written in about 20 minutes with two bug fixes discovered during hand iteration (needed to add bot to mid if lowering the upper bound and mid to mid if raising the lower bound). I’ve not tried it with a larger data set.

    class Array
      def bsearch(item)
        bot = 0
        top = self.size - 1
        mid = (top - bot) / 2
        idx = nil
    
        while bot < top and idx.nil?
          cur = self[mid]
    
          if cur == item
            idx = mid
            break
          end
    
          if item < cur
            top = mid - 1
            mid = (top - bot) / 2 + bot
          else
            bot = mid + 1
            mid = (top - bot) / 2 + mid
          end
    
          if mid > top
            mid = top
          elsif mid < bot
            mid = bot
          end
        end
    
        if bot == top and idx.nil? and self[bot] == item
          idx = bot
        end
    
        idx
      end
    
    end
    
    a = %w(1 2 3 4 6 7 8 10 11 12).map { |e| e.to_i }
    
    puts "3 == a.bsearch(4)? [#{a.bsearch(4)}]"
    puts "7 == a.bsearch(10)? [#{a.bsearch(10)}]"
    puts "nil == a.bsearch(9)? [#{a.bsearch(9)}]"
  180. Pingback: Are you one of the 10% of programmers who can write a binary search? « The Reinvigorated Programmer - Viewsflow

  181. C#

            [TestMethod]
            public void TestMethod1()
            {
                //build sample data
                int[] array = new int[100];
    
                for (int x = 0; x &lt; array.Length; x++)
                    array[x] = x * 2;
    
                int find = 14;
    
                //binary search
                int currentIndex = array.Length / 2 - 1;
                int upperIndex = array.Length - 1;
                int lowerIndex = 0;
    
                while (array[currentIndex] != find)
                {
                    int index = currentIndex;
    
                    if (lowerIndex == index
                        || upperIndex == index)
                        break;
    
                    if (array[index] &lt; find)
                    {
                        currentIndex = Convert.ToInt32(Math.Ceiling((upperIndex - index) / 2.0) + index);
                        lowerIndex = index;
                    }
                    else
                    {
                        currentIndex = Convert.ToInt32(Math.Floor((index - lowerIndex) / 2.0) + lowerIndex);
                        upperIndex = index;
                    }
    
                    Console.WriteLine(string.Format(&quot;[{0}]{1}&quot;, currentIndex, array[currentIndex]));
                }
    
                //result
                if(array[currentIndex] == find)
                    Console.WriteLine(&quot;found&quot;);
                else
                    Console.WriteLine(&quot;not found&quot;);
            }
    
  182. C#

            [TestMethod]
            public void TestMethod1()
            {
                //build sample data
                int[] array = new int[100];
    
                for (int x = 0; x < array.Length; x++)
                    array[x] = x * 2;
    
                int find = 14;
    
                //binary search
                int currentIndex = array.Length / 2 - 1;
                int upperIndex = array.Length - 1;
                int lowerIndex = 0;
    
                while (array[currentIndex] != find)
                {
                    int index = currentIndex;
    
                    if (lowerIndex == index
                        || upperIndex == index)
                        break;
    
                    if (array[index] < find)
                    {
                        currentIndex = Convert.ToInt32(Math.Ceiling((upperIndex - index) / 2.0) + index);
                        lowerIndex = index;
                    }
                    else
                    {
                        currentIndex = Convert.ToInt32(Math.Floor((index - lowerIndex) / 2.0) + lowerIndex);
                        upperIndex = index;
                    }
    
                    Console.WriteLine(string.Format("[{0}]{1}", currentIndex, array[currentIndex]));
                }
    
                //result
                if(array[currentIndex] == find)
                    Console.WriteLine("found");
                else
                    Console.WriteLine("not found");
            }
    
  183. @Mike – you could just provide test cases in pseudocode and leave it up to us to convert it to our language.

  184. untested and unrun. *fingers crossed*

    def binSearch(array, number):
    	"""recursive; returns the index in the original array which contains number"""
    	if len(array) == 0:
    		raise Exception("%d not found" % number)
    
    	if len(array) == 1:
    		if array[0] == number:
    			return 0
    		else:
    			raise Exception("%d not found" % number)
    
    	else:
    		mid = len(array) / 2
    		if array[mid] < number:
    			return binSearch(array[0:mid], number)
    		else:
    			return mid + binSearch(array[mid:], number)
    
  185. Pavel Panchekha
    def bsearch(l, n, offset=0):
        if not l:
            raise ValueError
        N = len(l)
        pivot = N/2 
        if n < l[pivot]:
            return bsearch(l[:pivot], n, offset)
        elif n == l[pivot]:
            return offset + pivot
        else:
            return bsearch(l[pivot+1:], n, offset + pivot + 1)
    

    Success!

  186. “Mike – you could just provide test cases in pseudocode and leave it up to us to convert it to our language.”

    I guess. But in part, too, I deliberately held off because I know that some of the test cases will immediately show people aspects of the problem that I wanted to see whether they’d spot for themselves. One obvious example is the zero-length-array test case: as Kernighan Pike say, “Make sure your code “does nothing” gracefully”. And, sure enough, a few commenters have been brave and honest enough to admit that they overlooked that case.

    There have been so many interesting comments here (and on Reddit and Hacker News) that I have lots of material for a followup article, probably to be posted tomorrow. That might include some abstract test-cases.

  187. Ok so I did this one in ColdFusion and I was pretty lazy but used recursion. Since there is no “array split” type function in CF I also wrote one of those to make my life easier. I have not tested it; beyond making sure CF doesn’t find a syntax error

    <cffunction name="binarySearch" returntype="boolean" output="false">
    	<cfargument name="ary"	type="array"	required="true" />
    	<cfargument name="val"	type="string"	required="true" />
    
    	<cfset var l = structNew() />
    
    	<cfif ArrayLen(arguments.ary) GT 1>
    		<cfif ArrayLen(arguments.ary) EQ 2>
    			<cfreturn VAL((arguments.ary[1] EQ arguments.val OR arguments.ary[2] EQ arguments.val))>
    		<cfelse>
    			<cfset l.pos = Round(ArrayLen(arguments.ary)/2) />
    			
    			<cfif arguments.ary[l.pos] EQ arguments.val >
    				<cfreturn 1 />
    			<cfelseif arguments.ary[l.pos] GT arguments.val>
    				<cfset arguments.ary = ArraySplit(arguments.ary, 1, l.pos-1) />
    			<cfelse>
    				<cfset arguments.ary = ArraySplit(arguments.ary, l.pos+1, ArrayLen(arguments.ary)) />
    			</cfif>
    
    			<cfreturn binarySearch(arguments.ary, arguments.val) />
    		</cfif>
    
    	<cfelse>
    		<cfif arguments.ary[1] EQ arguments.val>
    			<cfreturn 1 />
    		<cfelse>
    			<cfreturn 0/>
    		</cfif>
    	</cfif>
    
    
    </cffunction>
    
    <cffunction name="ArraySplit" returntype="array">
    	<cfargument name="ary"	type="array">
    	<cfargument name="sp"	type="numeric">
    	<cfargument name="ep"	type="numeric">
    
    	<cfset var l = structNew() />
    	<cfif arguments.ep LT arguments.sp>
    		<cfthrow message="End Point must be greater than or equal to the starting point" />
    	</cfif>
    	<cfif arguments.ep GT ArrayLen(arguments.ary)>\
    		<cfthrow message="End point can not be greater than the size of the array to split" />
    	</cfif>
    	<cfif arguments.sp GT arguments.sp>
    		<cfthrow message="Start Point must be less than or equal to the size of the array to split" />
    	</cfif>
    
    	<cfset l.a = ArrayNew(1) />
    	<cfloop from="#arguments.sp#" to="#arguments.ep#" index="l.i">
    		<cfset ArrayAppend(l.a, arguments.ary[l.i]) />
    	</cfloop>
    
    
    	<cfreturn l.a />
    
    </cffunction>
    
  188. Hi, did not read to the end and tested before posting. After posting I noticed the out of bound issues, guess that makes me part of the majority.

    def binary_search(a, r, T):
    while r != [] and r[0] != T:
    print r
    if T < mean(r):
    r = [r[0], mean(r)]
    else:
    r = [mean(r), r[1]]
    return r[0]

  189. This had a very silly off-by-one ((count v) instead of (dec (count v)) for the initial hi), so I failed. Seems to work now, though.

    (defn binary-search 
      ([v x] (binary-search v x -))
      ([v x cmp]
        (loop [lo 0 hi (dec (count v))]
          (cond (> lo hi) nil
                (= lo hi) (if (= 0 (cmp (v lo) x)) lo nil)
                true      (let [mid (quot (+ lo hi) 2)
                                dif (cmp x (v mid))]
                            (cond (= dif 0) mid
                                  (> dif 0) (recur (inc mid) hi)
                                  (< dif 0) (recur lo mid)))))))
    
  190. William Laffin

    Success!!! took me 40 minutes. I didn’t want to cheat, so I read other comments after I made it, turns out you are supposed to use squares not curlies (whoops)

    def bsearch(list,elem,key=None):
        if key is None:
            key = lambda x:x
        if len(list) > 0:
            return bsearchr(list,elem,0,len(list)-1,key)
        return -1
    
    
    def bsearchr(list, elem, i, j, key):
        if (j - i) < 2:
            if key(list[i]) == elem:
                return i
            if key(list[j]) == elem:
                return j
            return -1
        else:
            mindex = (j - i)/2 + i
            if key(list[mindex]) < elem:
                return bsearchr(list, elem, mindex, j, key)
            else:
                return bsearchr(list, elem, i, mindex, key)
    
  191. My submission:

    
    #return pos in case of sucess, -1 if didn't find it
    
    def bsearch(sortedList, n):
    	pos = 0
    	size = len(sortedList) 
    	
    	if(size == 0):
    		return -1
    	
    	if(size == 1):
    		if(sortedList[pos] != n): 
    			return -1
    		else:
    			return pos
    	
    	pos = size / 2
    	
    	if(sortedList[pos] == n):
    		return pos
    	else:
    		r1 = bsearch(sortedList[:pos],n) 
    		r2 = bsearch(sortedList[pos:],n) 
    		if(r1 != -1):
    			r1 = r1 + 0
    			return r1
    		elif r2 != -1:
    			r2 = r2 + pos
    			return r2
    		else:
    			return -1 
    
  192. James Birchall

    Seems to work…though it’ll report incorrectly if end < start. I thought about that condition after I ran the test (but without checking for the case or failing the test) so I'm not sure if that counts as a failure or not…given the way the code works, it should be an impossible input anyways but I figured it should be complete in isolation, without relying on the usage in main to be right (can't do much about whether the array is ordered or the len is specified correctly though…gotta love pointers, eh?).

    // Assumes list is ordered.
    int bs(unsigned int len, int list[], int val, unsigned int start, unsigned int end){
      // Preconditions
      if (end > len - 1) return -1;
      if (start < 0) return -2;
      if (end - start < 0) return -3;
    
      // Boundary checking short-circuit.
      // Saves function calls if the value isn't in the list at the expense
      // of slightly greater constant cost.
      if (list[start] > val) return -4;
      if (list[end] < val) return -5;
    
      unsigned int pos = start + ((end - start) >> 1);
      int pos_val = list[pos];
    
      if (pos_val == val) return pos;
      else if (pos_val < val) return bs(len, list, val, pos + 1, end);
      else return bs(len, list, val, start, pos - 1);
    }
    
    
  193. I see I also forgot to test the empty array case. So line 25 changes to

    		<cfif ArrayLen(arguments.ary) AND (arguments.ary[1] EQ arguments.val)>
    
  194. I *think* mine is correct.

  195. Vladimir Slepnev

    Did it in about 20 minutes in haXe, followed all your rules to the letter.

    static function binarySearch(array:Array<Float>, neededValue:Float)
    {
    	var lo:Int = 0;
    	var hi:Int = array.length;
    	
    	if (hi == 0)
    		return -1;
    	
    	while ((hi - lo) >= 2)
    	{
    		var mid:Int = Math.floor((lo + hi)/2);
    		var foundValue:Float = array[mid];
    		if (foundValue == neededValue)
    			return mid;
    		else if (foundValue < neededValue)
    			lo = mid;
    		else
    			hi = mid;
    	}
    	
    	if (array[lo] == neededValue)
    		return lo;
    	else
    		return -1;
    }
    
  196. Success, I think. SBCL’s compiler found a typo bug for me, but it worked the first time I tested it. I haven’t heavily tested it though. For large arrays (log(n) bigger than stack), an optimize declaration is required on bsearch to get most lisps to perform a TCO. I was aware of this before testing, and have only omitted it to make the code look cleaner.

    The 90% error rate doesn’t surprise me at all. Where I work, we have a rule of thumb: “If it hasn’t been tested, it doesn’t work” It is right a lot more than 90% of the time.

    If I got this right, it’s only because I’ve done it so many times before.

    (defun middle (a b)
      (truncate (/ (+ a b) 2)))
    
    (defun bsearch (a search start end)
      (let ((pivot (middle start end)))
        (cond
          ((= (aref a pivot) search) pivot)
          ((= start end) nil)
          ((< (aref a pivot) search)
           (bsearch a search (+ 1 pivot) end))
          (t (bsearch a search start pivot)))))
    
  197. Ops, I forgot to write the number of tries:

    4 tries / 20 minutes

  198. untested:

    have array x() with elements
    function to return index i of array x() which matches x(i) = y, else return -1
    zero based array
    x() is sorted such that x(i) <= x(i+1) for valid indices i, i+1

    function find(y)
    n = x.length();
    min = 0;
    max = n – 1;
    do while (min < max)
    test = (min + max) / 2
    if x(test) y then
    max = test – 1
    else
    return test
    end if
    loop
    return -1
    end function

  199. And another version in whitespace that worked right from the scratch:

  200. PS: time to complete was 18 minutes, 21 seconds according to git.

  201. Tada! My first ever python code. I tested it with 20000 randomly generated arrays, but I’m still not confident.

    def binaryS(array, sought, lower, upper):
    	guess = lower + int(math.floor(upper-lower)) / 2
    	if array[guess] == sought:
    		return guess
    	elif upper-lower >= 1:
    		if array[guess] < sought:
    			return binaryS(array, sought, guess+1, upper)
    		elif array[guess] > sought:
    			return binaryS(array, sought, lower, guess-1)
    	else:
    		return -1
    
    
  202. Code at (Annie // April 19, 2010 at 9:39 pm) doesn’t compile,
    the line
    T *middle = (first+last)/2;
    should be
    T *middle = first + (last-first)/2;

  203. # Mon Apr 19 14:51:14 PDT 2010 - started exercise
    # Mon Apr 19 14:58:12 PDT 2010 - submitting implementation pre-test,
    #                                so please be gentle.
    
    def bsearch(needle, haystack):
        remain = len(haystack)
        offset = 0
    
        while remain > 0:
            i = offset + (remain / 2)
    
            if haystack[i] == needle:
                return i
            else:
                if haystack[i] < needle:
                    offset = i
                remain -= ((remain / 2) or 1)
    
        return -1 # needle not found in haystack
    
  204. 18 min in C++. Works with STL random iterators and with any type T which is LessThanComparable.

  205. Mine works according to the included test. Very nearly messed up the midpoint calculation but caught it at the last minute before I ran the test.

    def binary_search(a,target):
    def search_range(begin,end):
    if end-begin == 0:
    return -1
    if end-begin == 1:
    if a[begin] == target:
    return begin
    else:
    return -1
    middle = (end+begin)/2
    if target < a[middle]:
    return search_range(begin,middle)
    else:
    return search_range(middle,end)

    return search_range(0,len(a))

    def test_search(a,target):
    ix = binary_search(a,target)
    if target in a:
    assert ix == a.index(target), "failed for target == %d" % target
    print target, ix
    else:
    assert ix == -1, "failed for target == %d" %target
    print target, ix

    a = range(0,100,3)
    for t in range(-2,105):
    test_search(a,t)

  206. (self smack for not reading markup instructions properly, this is my second attempt at posting)

    I prioritized legibility, went with recursion, had one bug that I fixed (I wasn’t checking array max index). Nice idea.

    #!/usr/bin/env ruby
    
    @elements_checked = []
    
    def seek_element( search_value, index_to_check, minimum_to_check, maximum_to_check, array )
      @elements_checked << index_to_check
    
      if array[index_to_check] == search_value
        return index_to_check
      end
    
      if search_value < array[index_to_check]
        next_index_to_check = (minimum_to_check + index_to_check) / 2
        maximum_to_check = index_to_check
      else
        next_index_to_check = (maximum_to_check + index_to_check) / 2
        minimum_to_check = index_to_check
      end
    
      if next_index_to_check == index_to_check
        if next_index_to_check == array.size - 2
          next_index_to_check = array.size - 1
        else
          return nil
        end
      end
    
      seek_element( search_value, next_index_to_check, minimum_to_check, maximum_to_check, array )    
    end
    
    
    array_size = ARGV[0].to_i
    random_range = ARGV[1].to_i
    search_value = ARGV[2].to_i
    
    puts "looking for #{search_value} amidst #{array_size} elements ranging from 0 to #{random_range - 1}"
    
    if random_range <= search_value
      puts "random_range needs to be greater than search_value"
      exit 0
    end
    
    sorted_array = (0..array_size).collect {|i| rand(random_range)}.sort
    
    first_index_to_check = sorted_array.size / 2
    first_minimum_to_check = 0
    first_maximum_to_check = sorted_array.size - 1
    
    index_with_value = seek_element( search_value, first_index_to_check, first_minimum_to_check, first_maximum_to_check, sorted_array )
    
    puts "Did #{@elements_checked.size } searches: #{@elements_checked.inspect}."
    if index_with_value
      sorted_array[index_with_value] = "**#{ sorted_array[index_with_value] }**"
      puts "Found on #{index_with_value}"
    else
      puts "Not present"
    end
    puts sorted_array.inspect
    
  207. def bsearch(lst, x, lo=None, hi=None):
        if lo is None:
            lo = 0
            hi = len(lst)-1
    
        if lo < hi:
            return None
    
        mid = (lo+hi)/2
        if x == lst[mid]:
            return mid
        elif x < lst[mid]:
            return bsearch(lst, x, lo, mid-1)
        else:
            return bsearch(lst, x, mid+1, hi)
    

    It did work the first time I ran it, much to my delight. However, I'm pretty sure that without the problem setup, designed to induce extreme paranoia, I would have failed. It's easy to see why 90% of the people in Bentley's test would have failed.

  208. def bsearch(arr, find):
    	low = 0
    	high = len(arr)
    
    	while True:
    		if high == low and arr[high] != find:
    			return -1
    
    		index = (high - low)/2 + low
    		if arr[index] == find:
    			return index
    		elif arr[index] > find:
    			high = index
    		else:
    			low = index
    

    Yeah, infinite loop in some cases when the result’s not found. Three-second fix, but we’re going for first try, huh.

    def bsearch(arr, find):
    	low = 0
    	high = len(arr)
    
    	while True:
    		index = (high - low)/2 + low
    		if arr[index] == find:
    			return index
    		elif index == low:
    			return -1
    		elif arr[index] > find:
    			high = index
    		else:
    			low = index
    
  209. I had an off-by-one error when initializing the upper bound, outside of that it was a success.

  210. I like the challenge, but what kind of a lunatic would write code before writing tests?

  211. Haven’t tested it at all. Think I have all the edge cases. Needs golfing.

    # searches a sorted array for a target                                                                                                       
    # assume array is sorted, contains numbers, and $target is also a number                                                                     
    
    sub bsearch (@$) {
        my (@array, $target) = @_;
    
        # sanity check for empty arrays                                                                                                          
        unless (scalar @array) {
            return undef;
        }
    
        my $low = 0;
        my $high = $#array; # always >= 0                                                                                                        
    
        # sanity check if $target is out of bounds                                                                                               
        if ($target < $array[$low] ||
            $target > $array[$high]) {
            return undef;
        }
    
        # we are guaranteed that $low <= $high                                                                                                   
        return _bsearch(\@array,$low,$high,$target);
    }
    
    sub _bsearch {
        my ($array, $low, $high, $target) = @_;
    
        my $mid = $low + int(($high - $low)/2); # assuming $low < $high, then $low <= $mid <= $high                                              
    
        # is the midpoint the number we're looking for? Then we're done.                                                                         
        if ($ar->[$mid] == $target) {
            return 1;
        }
        # were we searching over an range of a single index? Then we're done.                                                                    
        elsif ($high == $low) {
            return undef;
        }
        # is the midpoint higher than the target?                                                                                                
        # Then we search between the low end and the midpoint.                                                                                   
        # If $low == $mid, then $high == $low +1, so the next pass                                                                               
        # will be over a single index and will be caught by the previous clauses                                                                 
        elsif ($ar->[$mid] > $target) {
            return _bsearch($array, $low, $mid, $target);
        }
    
        # we know $ar->[$mid] < $target                                                                                                          
        # if $low == $mid, then $high = $low+1, so we can explicitly                                                                             
        # test $ar->[$high] as the remaining possible location of $target                                                                        
        elsif ($mid == $low) {
            return $ar->[$high] == $target;
        }
        # otherwise the midpoint was below the target and $mid > $low, so $high > $mid                                                           
        else {
            return _bsearch($array, $mid, $high, $target);
        }
    }
    
  212. bah, close. i put ” on line 14. once i fixed that, everything else seems to work fine.

  213. def binary_search(seq, elem):
        if seq is None or len(seq) == 0:
            return -1
        lower, upper = 0, len(seq) - 1
    
        while lower < upper and lower >= 0:
            index = upper / 2
            compare = cmp(elem, seq[index])
            if compare < 0:
                lower, upper = lower, index - 1
            elif compare > 0:
                lower, upper = index + 1, upper
            else:
                return index
        if seq[lower] == elem:
            return lower
        else:
            return -1

    This was my untested code.

    I got the calculation of index wrong, it should be index = lower + (upper – lower) / 2

  214. wow, wordpress is a terrible medium for this exercise. this should really be on a forum where it’s possible to have threaded comments, because the comment section here has become a complete clusterf*ck.

  215. Simple recursive Python version. Seems to work fine:

    def bs(array, value, first, last):
        print(array[first:last])
        if first == last and array[first] != value:
            return False
        else:
            middle = first + int((last - first)/2)
            if array[middle] == value:
                return middle
            elif array[middle] > value:
                return bs(array, value, first, middle)
            elif array[middle] < value:
                return bs(array, value, middle, last) 
    
  216. This was my javascript effort, seems to work okay:

    function binarySearch(array, T) {
    	if (array.length === 0)
    		throw 'not found';
    
    	var index = array.length / 2;
    	if (array.length % 2 === 1)
    		index -= .5;
    
    	if (array[index] < T)
    		return index + 1 + binarySearch(array.slice(index + 1), T);
    
    	if (array[index] > T)
    		return binarySearch(array.slice(0, index), T);
    
    	return index;
    }
    
  217. I wont post the code because you have 100s of examples, but for the sake of your “poll” – I did it in Python, coded it, reviewed it and than ran it without a single bug (Well I did slack off on writing meaningful exception messages :)

    Took me about 10 minutes.

    I hope to see the results soon, although I fear that they will be badly skewed.

  218. Failed really stupidily by returning the relative index (from the evaluated range) instead of the absolute index.

    Changed the code, ran the tests again and everything worked correctly. Woe is me.

    Used ruby, btw.

  219. Fun stuff :D

    phps, untested.

        function BinarySearch($pSortedArray, $pTarget)
        {
            $oIndex = 0;
            $oRangeFrom = 0;
            $oRangeTo = count($pSortedArray) - 1;
            while($oRangeFrom <= $oRangeTo)
            {
                $oIndex = floor( ($oRangeFrom + $oRangeTo ) /2 );
                
                if($pTarget > $pSortedArray[$oIndex])
                {
                    $oRangeFrom = $oIndex + 1;
                }
                else if($pTarget < $pSortedArray[$oIndex])
                {
                    $oRangeTo = $oIndex - 1;
                }
                else
                {
                    return($oIndex);
                }
            }
            
            return( -1 );
        }
    
    
  220. Wrote this in Ruby in about an hour. Since I couldn’t test before hand I made that final “if” block instead of actually figuring out what’s going on.

    It passes the tests that I came up with.

    def bin_search(arr,val)
      start_index = 0
      end_index = arr.length - 1
      mid_index = ((end_index - start_index) / 2) + start_index
    
      while mid_index != start_index do
        if arr[mid_index] > val
          end_index = mid_index
        elsif arr[mid_index] <= val
          start_index = mid_index
        end
        mid_index = ((end_index - start_index) / 2) + start_index
      end
    
      if arr[start_index] == val
        return start_index
      elsif arr[end_index] == val
        return end_index
      else
        return nil
      end
    end
    
    
  221. def bsearch(item, l, offset=0):
    if len(l) > 1:
    idx = len(l)/2
    p = l[idx]
    if p == item:
    return idx + offset
    elif p item:
    return bsearch(item, l[:idx], offset)
    else:
    if l[0] == item:
    return offset
    return None

  222. python:

    def find(v,T):
        "v is sorted; return i so that v[i]=T, or None if no such i exists"
        a=0
        b=len(v)
        # i in [a,b) if v[i]=T
        while a<b:
            m=a+(b-a)/2
            p=v[m]
            if p==T: return m
            if T<p:  b=m
            else:    a=m+1
        return None
    

    I’m 99.5% sure there’s no bug in the above. I was aware already of the integer overflow complaint, which is the only reason I didn’t use (a+b)/2.

    A more interesting task would be to return the largest range [a,b) such that v[i]=T for a<=i<b

  223. Dan Lidral-Porter

    First Erlang implementation, it seems.

    -module(bsearch).
    -export([bsearch/2, random_sorted_list/1]).
    
    bsearch(List, N) ->
    	io:format("searching ~w for ~w~n", [List, N]),
    	bs_rec(List, N, 1,length(List)).
    
    
    bs_rec(List, Goal, Start, Stop) ->
    	Mean = erlang:trunc((Start + Stop) / 2),
    	Nth = lists:nth(Mean, List),
    	
    	if
    		Start == Stop ->
    			if 
    				Nth == Goal ->
    					{Goal, Mean};
    		
    				true ->
    					not_found
    			end;
    	
    		Nth < Goal ->
    			bs_rec(List, Goal, Mean + 1, Stop);
    		
    		Nth == Goal ->
    			{Goal, Mean};
    		
    		Nth > Goal ->
    			bs_rec(List, Goal, Start, Mean-1)
    	end.
    

    When I was scrolling down to post, I saw a comment that reminded me that I forgot about the 0-length case. Darn.

    If I add a guard in bsearch() to return the not_found atom if the length of the list is 0, then I think I passed. At least, I’ve tried all the cases I can think of and it works fine.

  224. Ok, here’s mine. 25min and completely untested, thus probably horribly wrong and/or buggy (I usually copy/paste even my email address because I tend to mistype it…):

    #!/usr/bin/ruby
    
    $nComp = 0
    
    def binSearch(array, el, mi, ma)
    	$nComp += 1
    
    	# out of range - error
    	if el < array[mi] || el > array[ma]
    		return nil
    		end
    
    	# found it!
    	if el == array[mi]
    		return mi
    		end
    	if el == array[ma]
    		return ma
    		end
    
    	# end of search and not found - error
    	if mi >= ma-1
    		return nil
    		end
    
    	half = (mi + ma)/2
    	
    	if el < array[half+1] 
    		binSearch(mi+1, half)
    	else
    		binSearch(half+1, ma-1)
    		end
    	end
    
    
    nElements = ARGV[0].to_i
    
    arr = []
    
    nElements.time do
    	arr << rand(100)
    	end
    
    arr.sort!
    
    puts "array:"
    puts arr.join(" ")
    
    elem = 0
    
    puts "enter element to search for: "
    $stdin >> elem
    
    puts "looking for " + elem.to_s 
     
    ret = binSearch(arr, elem, 0, arr.length-1)
    
    puts $nComp.to_s + " comparisons"
    
    if ret == nil
    	puts "element " + elem.to_s + " not found!"
    else
    	puts "element " + elem.to_s + " found at " + ret.to_s + "!"
    	end
    
  225. Did it in Haskell in 5 minutes, stupidly left out one base case and found it with the second test case. I admire Matthias Goergens’s solution for being a bit more Haskell-ey than mine.

  226. Try again, with square brackets this time.

    In Python (2.6 flavor). Untested.

    def binary_search(sequence, target):
        """Return the index in the sorted sequence of target or None if target
        is not in sequence."""
        
        start_i = 0
        end_i = len(target)
        
        while True:
            if end_i <= start_i:
                return None
            
            length = end_i - start_i
            
            midpoint_i = start_i + length / 2
            
            midpoint = sequence[midpoint_i]
            
            if midpoint == target:
                return midpoint_i
            
            if midpoint > target:
                end_i = midpoint_i
            else:
                start_i = midpoint_i + 1
    
  227. Joshua Leners

    I failed.

  228. Tested, no bugs found… yehaa :)

    int binary_search(vector<int>& arr, int needle)
    {
    	/// range is at the beginning = entire array
    	int lower = 0;
    	int upper = arr.size()-1;
    	int middle = 0; // will be set in loop
    	
    	/// if lower == upper the range is 1
    	while(lower < upper)
    	{
    		// find the middle
    		middle = floor((upper-lower)/2.0) + lower;
    		
    		if(arr[middle] == needle)
    			return middle; // yay, found it!
    		
    		if(arr[middle] > needle)
    		     // middle is to big => kill upper half
    			 // middle itself is also out of range => -1
    			upper = middle-1;
    		else // middle is to small => kill lower half
    			lower = middle+1;
    	};
    	if(lower == upper // just to make sure, array wasn't empty
    		and arr[lower] == needle)
    			return lower; // gotcha!
    	
    	throw "not found";
    	return 0;
    }
    

    Well, it looks like, there will be much too much code to review ;)
    It certainly was interesting. It certainly looks easy but there are some pitfalls.

  229. I failed my first run because of two typos which resulted in a logic error, so I added a case tester to debug and correct. Seems to work for all the cases I could think of.

    sub b_search($$;$$) {
        my( $ary, $search, $x, $y ) = @_;
        # helper
        if ( !defined( $x ) || !defined( $y ) ) {
            return b_search($ary, $search, 0, $#{$ary} );
        }   
        if  ( $x == $y ) {
            return $x if ( $ary->[$x] eq $search );
            return -2;
        } elsif ( $x > $y ) {
            return -3;
        }
        my $mid = int(($y-$x)/2) + $x;
        return $mid if $ary->[$mid] eq $search;
        if ( $search lt $ary->[$mid] ) {
            return b_search( $ary, $search, $x, $mid - 1 );
        } else {
            return b_search( $ary, $search, $mid + 1, $y );
        }
        return -1;
    }
    
  230. What constitutes testing? Just running the code? What about using pencil and paper to help you test it by eye? I wrote the whole thing, but found one obvious bug really quickly just by looking at it. Then I did some testing with an example array and used paper to help track values and found another bug.

    I wrote up unit tests in the code then ran it. Output: “Done. Press any key.” Total time = 1 hour. Language = C#.

    It may not be as elegant as other posted algorithms. Rather than just checking the midpoint, it also checks the start and end at the same time. It might make more comparisons than is necessary in some cases, but it also makes far fewer comparisons in other cases (where the value you want is in a very early or late position of a large array.) It’s probably a wash. It at least never compares the same position more than once.

    class Program
    {
        static void Main( string[] args )
        {
            int[] values1 = new int[] { };
            int[] values2 = new int[] { 2 };
            int[] values3 = new int[] { 2, 3 };
            int[] values4 = new int[] { 2, 3, 5, 7 };
            int[] values5 = new int[] { 2, 3, 5, 7, 11 };
            int[] values6 = new int[] { 2, 3, 5, 7, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 };
    
            TestBinarySearch( values1, 7, -1 );
            TestBinarySearch( values1, 0, -1 );
    
            TestBinarySearch( values2, 2, 0 );
            TestBinarySearch( values2, 1, -1 );
            TestBinarySearch( values2, 3, -1 );
    
            TestBinarySearch( values3, 1, -1 );
            TestBinarySearch( values3, 4, -1 );
            TestBinarySearch( values3, 2, 0 );
            TestBinarySearch( values3, 3, 1 );
    
            TestBinarySearch( values4, 1, -1 );
            TestBinarySearch( values4, 3, 1 );
            TestBinarySearch( values4, 5, 2 );
            TestBinarySearch( values4, 7, 3 );
            TestBinarySearch( values4, 8, -1 );
    
            TestBinarySearch( values5, 1, -1 );
            TestBinarySearch( values5, 15, -1 );
            TestBinarySearch( values5, 5, 2 );
            TestBinarySearch( values5, 11, 4 );
    
            TestBinarySearch( values6, 1, -1 );
            TestBinarySearch( values6, 103, -1 );
            TestBinarySearch( values6, 5, 2 );
            TestBinarySearch( values6, 53, 14 );
            TestBinarySearch( values6, 17, 5 );
    
            Console.WriteLine( "Done. Press any key." );
            Console.ReadLine();
        }
    
        static int testNumber = 0;
    
        private static void TestBinarySearch( int[] values, int target, int expectedResult )
        {
            testNumber++;
    
            int position = BinarySearch( values, target );
    
            if ( position != expectedResult )
            {
                Console.WriteLine( String.Format( "Unexpected result in test {0}. Expected: {1}. Received: {2}.", testNumber, expectedResult, position ) );
            }
        }
    
        private static int BinarySearch( int[] values, int target )
        {
            if ( values.Length == 0 )
            {
                return -1;
            }
    
            return BinarySearch( values, target, 0, values.Length - 1 );
        }
    
        private static int BinarySearch( int[] values, int target, int iStart, int iEnd )
        {
            if ( values[ iStart ] == target )
            {
                return iStart;
            }
            else if( values[ iEnd ] == target )
            {
                return iEnd;
            }
            else if ( values[ iEnd ] < target || values[iStart] > target )
            {
                return -1;
            }
    
            int iMid = ( ( iEnd - iStart + 1 ) / 2 ) + iStart;
    
            if ( values[ iMid ] == target )
            {
                return iMid;
            }
            else if ( values[ iMid ] < target )
            {
                return BinarySearch( values, target, iMid + 1, iEnd - 1 );
            }
            else
            {
                return BinarySearch( values, target, iStart + 1, iMid - 1 );
            }
        }
    }
  231. I tested it against one array quickly…and it failed. Had two bugs, both 1 character long. First I initially passed the array by reference (so an extra &) in order not to have to copy the array, but I guess you can’t do that in PHP. Second, I accidentally substracted the end and starts to get the average instead of dividing.

    Took me ~15 minutes. Sloppy code and not sure if it actually works.

    Cool experiment.

    <?php
    function binary_search($sorted_array, $T)
    {
    	$start = 0;
    	$end = count($sorted_array) - 1;
    	while ($end - $start > 0)
    	{
    		if ($end - $start == 1)
    		{
    			if ($sorted_array[$end] == $T)
    			{
    				return $end;
    			}
    			elseif ($sorted_array[$start] == $T)
    			{
    				return $start;
    			}
    			return -1;
    		}
    		$halfway = ceil(($end + $start)/2.0);
    		if ($T == $sorted_array[$halfway])
    		{
    			return $halfway;
    		}
    		elseif ($T > $sorted_array[$halfway])
    		{
    			$start = $halfway;
    		}
    		else
    		{
    			$end = $halfway;
    		}
    	}
    	return ($sorted_array[0] == $T ? 0 : -1);
    }
    
  232. Well… Test results: 1 type, 1 Ruby fail (who ever uses input from the terminal ever…), 1 forgotten args in the recursion (D’OH!). But the algorithm works quite fine (although I do *not* check the 0-array case).

  233. Submitted without testing or reading the comments:

    # Return undef if $T not found in @$list.
    # Otherwise, return index of position of $T in @$list.
    # If $T appears more than once, any valid index may be returned.
    # @$list must be pre-sorted.
    
    sub binary_search {
        my ( $T, $list ) = @_;
    
        # Invalid arguments, empty list
        return unless defined $T
            && defined $list
            && ref $list eq 'ARRAY'
            && @$list > 0;
    
        # $pos is midpoint, more or less. Err on the low side.
        my $pos = int( scalar(@$list) / 2);
    
    
        # Comparison; assume strings
        # $cmp < 0 if $T lt $list->[$pos]
        # $cmp == 0 if $T eq $list->[$pos]
        # $cmp > 0 if $T gt $list->[$pos]
        my $cmp = $T cmp $list->[$pos];
    
        # Good guess
        return $pos if $cmp == 0;
    
        if ( $cmp < 0 ) {
            return if $pos == 0;    # no elements on left
            my @new_list = splice( @$list, 0, $pos-1 );
            return binary_search( $T, \@new_list );
        }
        else {
            return if $pos+1 == scalar(@$list);     # no elements on right
            my @new_list = splice( @$list, $pos+1 );
            my $new_pos = binary_search( $T, \@new_list );
            # If $new_pos is undef, $T wasn't found on the right.
            # Otherwise, $new_pos is the position in @new_list, which is offset
            # from the position in $list by $pos+1 because of the splice arguments.
            if ( defined $new_pos ) {
                return $new_pos + $pos + 1;
            }
            else {
                return;
            }
        }
    }
    
  234. I got it right on the first try. Yay for the 10%.

    Note that your results will skew for success anyway, for several reasons. One is that people who read programming blogs are skewing for the better end, but the more important thing is that some unsuccessful people will not post, no matter what you told them.

    import random
    arr = range(1000)
    x = random.randrange(1000)
    
    def binsearch(arr, x):
        print "searching for %d" % x
        def binsearch_aux(arr, x, min, max):
            print "  is between %d and %d" % (min, max)
            if arr[min] == x:
                print "  found at %d" % min
                return min
            middle = (min + max) / 2
            pivot = arr[middle]
            if pivot <= x:
                return binsearch_aux(arr, x, middle, max)
            else:
                return binsearch_aux(arr, x, min, middle)
    
        if arr[0] < x > arr[-1]:
            print "  element is not in array"
            return None
        
        return binsearch_aux(arr, x, 0, 1000)
    
    # test cases
    binsearch(arr, 500)
    binsearch(arr, 501)
    binsearch(arr, 502)
    binsearch(arr, 499)
    binsearch(arr, 1)
    binsearch(arr, 0)
    binsearch(arr, 999)
    binsearch(arr, x)
    binsearch(arr, 1000)
    
    arr = list()
    for i in range(1000):
        arr.append(random.randrange(100000000))    
    arr.sort()
    print "starting random test"
    for x in range(1000):    
        if binsearch(arr, arr[x]) != x:
            print "MISSING AT %d" % x
            print arr
            import sys
            sys.exit(-1)
    
    
  235. Fencepost error, used “pivot = min” instead of “pivot = min + 1”. Infinite loop if no match. Worked when I fixed it, no boundary errors, remembered to check for an empty array.

  236. Dan Lidral-Porter

    My implementation also fails on duplicate keys. Oops.

  237. example1 = ['a', 'b', 'c', 'd', 'e', 'f']
    example2 = ['a', 'c', 'd', 'e', 'f']
    example3 = ['b', 'f', 'f', 'f', 'f', 'f']
    
    def binsearch(arr, findval):
        ''' return findval, or None if not found'''
    
        while 1:
            mid = len(arr)/2
            if len(arr) == 0: return -1
            if len(arr) == 1 and arr[0] != findval: return -1
    
            if arr[mid] > findval :
                #LHS
                arr = arr[:mid]
            elif arr[mid] < findval :
                #RHS
                arr = arr[mid:]
            elif arr[mid] == findval:
                return arr[mid]
    
    print binsearch(example1, 'b')
    print binsearch(example2, 'b')
    print binsearch(example3, 'z')
    

    A couple of comments,
    1. I put in a couple of tentative tests at entry the second is probaby not needed
    2. I totally failed to return the actual index in original array

    I tried rewriting recursively and messed up the indexing and completely missed using low/high to compress a window.

    so I got it right but I think I failed.
    tidied up my comments after testing.
    20 mins.

  238. I was able to do it, including the ‘overflow’ dangers mentioned on the Google blog. I used pointers instead of indexes so I made sure of this

  239. So, tentatively, I want to say that I passed the test. Submission is above and here: http://gist.github.com/371735

    Unfortunately, as noted in my gist, I don’t know what I don’t know! I might be iterating too many times, and I don’t know which other cases I should be testing against. Someone needs to put together a test suite for the numerous Python submissions!

  240. Aaand there it is: Failure to read the specification. I only search for values in the array. Cheers for me, I’m part of the 90%.

  241. I had one bug that prevented me from identifying the value stored at the end of the array i.e. the largest value. Fixing it was pretty simple.

    I have the 2nd edition of the book and every year and a half or so I come across this very section and end up re-coding it. Usually, I make a careless error. Never takes more than a few minutes to fix.

  242. Argh… my first call to splice should use $pos rather than $pos-1 because it’s the length of the sub-list rather than the position of the end of the sub-list.

  243. Here’s my attempt at a recursive version in ruby.

    def binary_search(array, element, low = 0, high = array.length-1)
      raise ArgumentError unless Range.new(0,array.length-1).include? low
      raise ArgumentError unless Range.new(0,array.length-1).include? high
      
      return high if array[high] == element
      return low if array[low] == element
      return nil if low >= high
      
      mid = (low+high)/2
      return binary_search(array, element, low, mid) if element <= array[mid]
      return binary_search(array, element, mid+1, high) if element > array[mid]
      nil
    end
    
  244. I think my attempt works. I first wrote it as a “contains” check before I realized the point of the task was to find the right index. Because I didn’t think enough before patching it accordingly I actually created an off-by-one error.

    I find the requirements a bit harsh, though, because I’ve made it a habit to code with a Python shell open next to my text editor and writing in both, testing my logic and assumptions as I write the code.

    I guess I’m not one of the 10 percent then, if only because I test my code rather than mentally parsing it line for line to check for errors prior to testing.

    def search(seq, k):
        j = 0
        while len(seq) > 0:
            i = len(seq)//2
            if seq[i] == k:
                return j+i
            if seq[i] > k:
                seq = seq[:i]
            else:
                seq = seq[i+1:]
                j += i+1
        return None
    
  245. Jaisen Mathai

    {source}var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19];
    var count = 0;

    function bsearch(arr, lookup)
    {
    console.log(arr);
    count++;
    if(count > 100)
    return ‘endless recursion?’;

    if(arr.length == 1)
    return arr[0] == lookup ? arr[0] : false;

    var midpoint = parseInt(arr.length / 2);
    if(arr[midpoint] == lookup) {
    return arr[midpoint];
    } else if(arr[midpoint] > lookup) {
    return bsearch(arr.slice(0, midpoint), lookup);
    } else {
    return bsearch(arr.slice(midpoint, arr.length), lookup);
    }
    }

    console.log(bsearch(arr, 9));{/source}

  246. Correction. My code did not work with an empty list.

    Here is the corrected code.

    [SOURCE]
    template
    RIt binary_search(const RIt &first, const RIt &last, const T &val)
    {
    RIt a = first;
    RIt b = last;
    for(;;)
    {
    RIt::difference_type diff = b-a;
    if (diff==1)
    {
    if (*a==val) return a;
    return last;
    }
    RIt c = a+diff/2;

    if (val<*c) b = c;
    else a = c;
    }
    }
    [/SOURCE]

    Works with any type T that is less than comparable. And it works with random access iterators (such as pointers or vector iterators).

  247. template
    RIt binary_search(const RIt &first, const RIt &last, const T &val)
    {
    RIt a = first;
    RIt b = last;
    for(;;)
    {
    RIt::difference_type diff = b-a;
    if (diff==1)
    {
    if (*a==val) return a;
    return last;
    }
    RIt c = a+diff/2;
    
    if (val<*c) b = c;
    else a = c;
    }
    }
    
  248. Daniel Rogers

    This one is in javascript. Untested. 45 minutes (approx).

    function binary_search(t,a) {
    return binary_search_helper(t,a,0,a.length);
    }

    function binary_search_helper(t,a,start,end) {
    /*
    * assume start is untested
    * assume end is also tested and does not contain the element
    */
    if (start === end) {
    return null;
    }
    /*
    * only one element left to find
    */
    if (end-start === 1) {
    return t === a[start]?a[start]:null;
    }
    /*
    * end-start is at least 2 (since it is neither 0 or 1
    * from the conditions above), thus Math.floor((end-start)/2)
    * >=1.
    *
    * this is important to prevent an infinite loop
    */
    var middle = start + Math.floor((end-start)/2);
    /*
    * a[middle-1] makes sure we look for the first occurance
    * middle>=1 since end-start is at least 2 and start is >=0
    */
    if (a[middle] === t && a[middle-1] != t) {
    return middle;
    }
    if (a[middle] <== t) {
    return binary_search(t,a,middle+1,end);
    }
    /*
    * also covers the case where a[middle-1] === t
    * (i.e. we found the element but it's not the least
    * element)
    */
    return binary_search(t,a,start,middle);
    }

  249. Daniel Hanson

    Submitted without testing. The function returns the length of the array if the value cannot be found.

    size_t bsearch(int *array, size_t length, int value)
    {
        size_t begin = 0;
        size_t end = length;
    
        while (begin < end)
        {
            size_t mid = (end - begin) / 2 + begin;
            if (value < array[mid]) end = mid;
            else if (array[mid] < value) begin = mid + 1;
            else return mid;
        }
        
        return length;
    }
  250. Third times a charm? Delete my other comments please.

    I forgot to test for an empty list. So my code wasn’t correct the first time around as I had thought.

    template<class RIt, class T>
    RIt binary_search(const RIt &first, const RIt &last, const T &val)
    {
      if (first==last) return last;  // I had forgotten this line.
      RIt a = first;
      RIt b = last;
      for(;;)
      {
        RIt::difference_type diff = b-a;
        if (diff==1)
        {
          if (*a==val) return a;
          return last;
        }
        RIt c = a+diff/2;
    
        if (val<*c) b = c;
        else a = c;
      }
    }
    

    Works with any type T that is less than comparable. And it works with random access iterators (such as pointers or vector iterators).

    [Mike says: if you really want me to, I will delete your other comments. But I would prefer to retain them, because your successive iterations are instructive.]

  251. I failed. Got updating the pivot index wrong the first time.

    My current code succeeds with my tests. (And is ugly…)

    Time to first version: ~10 min
    Time to fix bugs: ~20 min

    #define FAIL do { return -1; } while (0)
    
    int binsrch(int *a, int len, int v)
    {
            int low = 0;
            int high = len-1;
    
            // zero element array?
            if (high < low)
                    FAIL;
    
            int diff = high - low;
            int p = low + diff / 2;
    
            do {
                    // success!
                    if (a[p] == v)
                            return p;
    
                    if (v < a[p])
                            high = p-1;
                    else
                            low = p+1;
    
                    diff = high - low;
                    p = low + diff/2;
    
            } while (diff > 0);
    
            if (a[p] == v)
                    return p;
    
            FAIL;
    }
    
  252. The really humbling thing isn’t how hard it is to write correct code without testing, but how long it takes, even for a simple textbook algorithm. Binary search is the sort of thing that sounds like it should take five minutes, but I took 45, and I don’t think I was distracted for more than 5 minutes of that. At least a third of the time was due to the added complexity of usefully supporting arbitrary types and orderings, not just numbers in ascending order. Here it is (in Common Lisp):

    (defun bsearch (target vec &key (key #'identity) (order #'<) (test #'eql))
      "Given a vector VEC sorted in ORDER by KEY, return an element whose KEY is equal (by TEST) to TARGET, or NIL.
    ORDER must be a total ordering - equivalent elements are not allowed.
    Examples:
      (bsearch 1/2 #(-1 3 2 1) :key #'/ :test #'=) => 2
      (bsearch 5 #(1 2 4 8)) => nil
      (bsearch 1 #((2 two) (1 one)) :key #'car :order #'>) => (1 ONE)
      (bsearch 1 #(() (a) (a a)) :key #'length :test #'equalp) => (A)"
      (labels ((bs (start end)
    	     (if (>= start end)
    		 nil
    	       (let* ((probe (floor (+ start end) 2))
    		      (pivot (funcall key (elt vec probe))))
    		 (cond ((funcall test target pivot) (elt vec probe))
    		       ((funcall order target pivot) (bs start probe))
    		       (t (bs (1+ probe) end)))))))
        (bs 0 (length vec))))

    It has passed all my tests so far. However, I’m not sure this counts entirely as a success, because I reinterpreted the requirements to make it easier: I originally intended to allow equivalent elements in the ordering, but when I realized that was awkward, I just gave up and declared that the ordering had to be total.

  253. Chris Lamberson

    I’ll try my hand at this. Here’s the code *before* testing (python):

    def binsearch(lst, target):
        pivot = len(lst)/2
        left, center, right = lst[:pivot], lst[pivot], lst[pivot+1:]
        if target == center:
            return center
        elif target < center:
            return binsearch(left, target)
        else:
            return binsearch(right, target)
    

    Let me know if I screwed it up! Pretty sure it works though…

  254. Dan Lidral-Porter

    I believe this code is correct . Ultimate shame on me if there are any bugs remaining.

    
    -module(bsearch).
    -export([bsearch/2).
    
    bsearch(List, N) ->
    	io:format("searching ~w for ~w~n", [List, N]),
    	Length = length(List),
    	if
    		Length == 0 ->
    			not_found;
    		
    		true ->
    			bs_rec(List, N, 1,length(List))
    	end.
    
    
    bs_rec(List, Goal, Start, Stop) ->
    	Mean = erlang:trunc((Start + Stop) / 2),
    	Nth = lists:nth(Mean, List),
    	
    	if
    		Start == Stop ->
    			if 
    				Nth == Goal ->
    					{Goal, Mean};
    		
    				true ->
    					not_found
    			end;
    			
    		Start > Stop ->
    			not_found;
    	
    		Nth < Goal ->
    			bs_rec(List, Goal, Mean + 1, Stop);
    		
    		Nth == Goal ->
    			{Goal, Mean};
    		
    		Nth > Goal ->
    			bs_rec(List, Goal, Start, Mean-1)
    	end.
    
  255. PHP:

    <?php
    	function binarySearch($needle, $haystack) {
    		$start = 0;
    		$end = count($haystack);
    		while (($end - $start) > 1) {
    			$mid = $start + floor(($end - $start) / 2);
    			if ($haystack[$mid] > $needle) {
    				$end = $mid;
    			} elseif ($haystack[$mid] < $needle) {
    				$start = $mid;
    			} else {
    				return $mid;
    			}
    		}
    		return false;
    	}
    ?>

    About 12-14 minutes.

  256. PHP, didn’t check this at all, thought it would be fun.

    <?php
    function bsearch($search, $array, $start, $end){
        //$start cannot be zero. -1 = not found, otherwise returns index of $search in $array
        //$search not in $array
        if($start>$end){
            return -1;
        }
        //calculate middle
        $middle = $start + ceil(($end-$start)/2);
        //check match
        if($array[$middle]==$search){
            return $middle;
        }
        //is the middle higher than the search, if so, cut top
        else if($array[$middle]>$search){
            return bsearch($search, $array, $start, $middle-1);
        }
        //otherwise cut bottom
        else{
            return bsearch($search, $array, $middle+1, $end);
        }
    }
    
    
    ?>
    
  257. John Gunderman

    here’s my solution, in Haskell:

    bsearch :: (Ord a) => a -> [a] -> Bool
    bsearch elem (x:[]) = if x == elem 
                          then True 
                          else False
    bsearch elem xs = if elem >= head second
                      then bsearch elem second
                      else bsearch elem first
        where mid = length xs `div` 2
              t = splitAt mid xs
              first = fst t
              second = snd t
    

    This works properly. However, I must admit I initially failed. The mistake was completely stupid as well; in the line reading

    t = splitAt mid xs
    

    I accidentally wrote this instead:

    t = splitAt 0 xs
    

    Which makes me feel quite stupid :) fortunately, that seems to be the only error I can find. Let me know if you find any other errors.

  258. How about a nice Fortran implementation :-)

    PROGRAM bs
    
    INTEGER :: list(12) = (/1, 3, 6, 11, 125, 345, 985, 1011, 2111, 2122, 3000, 4051 /)
    INTEGER :: target   = 11
    INTEGER :: ans      = -1
    
    CALL search(1, SIZE(list))
    
    PRINT *,ans
    
    CONTAINS
    
    RECURSIVE SUBROUTINE search(lower, upper)
    
       INTEGER :: middle, upper, lower
    
       middle = lower + NINT((upper - lower)/2.0)
    
       IF (list(middle) == target) THEN
          ans = middle
          RETURN
       ELSE IF(list(middle) &gt; target) THEN
          CALL search(lower, middle)
       ELSE IF(list(middle) &lt; target) THEN
          CALL search(middle, upper)
       END IF
    
       END SUBROUTINE search
    
    END PROGRAM bs
    
    
  259. I ran a few small tests – seems to work ok. Writen in C.

    int bin_search(int a[], size_t sz, int item)
    {
    int l_bound = 0;
    int u_bound = sz-1;
    int current;

    if (item == a[l_bound]) return l_bound;
    if (item == a[u_bound]) return u_bound;
    while (l_bound + 1 < u_bound) {
    current = (l_bound + u_bound)/2;
    if (item == a[current]) return current;
    else if (item < a[current]) u_bound = current;
    else l_bound = current;
    }
    return -1;
    }

  260. Did half-assed testing (which passed). No code changes after testing

    def bsearch(val, sorted_seq):
        """
        Use a binary search on sorted_list to find val.
    
        Args:
            val: a value that could appear in sorted_list.
            sorted_seq: some sort of container that supports __len__ and __getitem__
        Returns:
            either the integer index the element is at or None
        """
    
        # pre-loop
        try:
            assert val <= val
            assert val >= val
            assert val == val
        except AssertionError:
            raise ValueError("val does not pass basic equality tests")
    
        min_index = 0
        max_index = len(sorted_seq)
    
        # simple loop - no optimization checks
        while min_index < max_index:
            mid = (max_index + min_index) / 2
    
            mid_val = sorted_seq[mid]
            if val == mid_val:
                return mid
            elif val < mid_val:
                max_index = mid - 1
            elif val > mid_val:
                min_index = mid + 1
            else:
                raise ValueError("val does not properly compare to element %s" % str(mid_val))
    
        # couldn't find val
        return None
    
  261. In Python, using slicing and tail recursion. Passes unit tests of all the usual suspects.

    No guarantees of efficiency

    from __future__ import division                                                                                                                                
                                                                                                                                                                   
    import math                                                                                                                                                    
                                                                                                                                                                   
    def binsearch(element, mylist):                                                                                                                                     
        return find_helper(element, mylist, 0)                                                                                                                     
                                                                                                                                                                   
    def binsearch_helper(element, mylist, first_index):                                                                                                                 
        length = len(mylist)                                                                                                                                       
        if length == 0: return None                                                                                                                                
        if length == 1:                                                                                                                                            
            if mylist[0] == element: return first_index                                                                                                            
            else: return None                                                                                                                                      
        mid = int(math.floor(length / 2))                                                                                                                          
        if mylist[mid] == element: return mid + first_index                                                                                                        
        if element < mylist[mid]: return find_helper(element, mylist[0:mid], first_index)                                                                          
        return find_helper(element, mylist[mid:], first_index + mid)                                                                                               
    
  262. bey0ndy0nder

    Okay, I wrote it out on paper and worked out a few cases, then I just went out and wrote code + some tests. Probably has some bugs. And it’s ugly also. I fully admit that is a hack.

    //tail recursion
    int binSearch(int lo, int hi, int range, int theNumber, int sortedArray[])
    {
    int retIdx = -1;
    if(range==2) //sort of whack…
    {
    if(sortedArray[lo] == theNumber)
    return lo;
    else if(sortedArray[hi] == theNumber)
    return hi;
    else
    return -1; //not found
    }
    else if(range == 1)
    {
    if(sortedArray[lo] == theNumber)
    return lo;
    else
    return -1;
    }
    int divider = lo + range/2;
    if(sortedArray[divider] == theNumber)
    return divider;
    else if(theNumber < sortedArray[divider]) //recurse left
    {
    return binSearch(lo,divider-1,divider-lo,theNumber,sortedArray);
    }
    else //recurse right
    {
    return binSearch(divider+1,hi,hi-divider,theNumber,sortedArray);
    }
    }

    Tests:

    random_shuffle(randIdx.begin(), randIdx.end());
    
        cout << "Sorting array to prepare for binary search. " << endl;
        std::sort(massiveArrayOfInts, massiveArrayOfInts+massiveNumber);
    
        //First, try to find all matches in the array.
        foundCount = 0;
        for(int i=0; i < massiveNumber; ++i)
        {
            int retIdx = binSearch(0,massiveNumber-1,massiveNumber,massiveArrayOfInts[randIdx[i]],massiveArrayOfInts);
            if(retIdx >= 0)
                foundCount++;
        }
    
        cout << "Done binSearch. Foundcount is: " << foundCount << endl;
        if(foundCount == massiveNumber)
        {
    
            cout << "Foundcount: " << foundCount << " this means you win! A MASSIVE WIN! (Are you sure?)" << endl;
        }
    
  263. Wrong! But it’s very late and i’m coding while lying in my bed, not in the office :P

    (defun binary-search (elt list)
      (binary-search-1 elt list 0 (- (length list) 1)))
    
    (defun binary-search-1 (elt list first last)
      (cond
       ((= first last)
        (if (= elt (nth first list)) first nil))
       ((< first last)
        (let ((mid (/ (+ first last) 2)))
          (cond
           ((= elt (nth mid list)) mid)
           ((< elt mid)
    	(binary-search-1 elt list first (- mid 1)))
           (t
    	(binary-search-1 elt list (+ mid 1) last)))))))
    
    
  264. Okay, I came up with a nominally tested ruby implementation using recursion. Had an off by one error that led to infinite recursion, but I think that’s fixed now.

    def binsearch x, arr
        if arr.size == 0
            false
        elsif arr[arr.size/2] == x
            true
        else
            arr[arr.size/2] > x ? binsearch(x,arr[0...arr.size/2])
                                : binsearch(x,arr[(arr.size+1)/2...arr.size])
        end
    end
    
    
  265. Okay, about to test. I should say I’ve written binary search a number of times in the past and got it wrong a number of ways and fixed it. So I have specific personal rules (which I won’t reveal at this time) about writing binary search!

    But in this case I first wrote a very simple linear_search() function, then a tester, then a broken_search() function to make sure the tester actually catches the bugs it’s looking for. In that step I came across a bug I hadn’t tested for: not returning the ( i, nsteps ) tuple that the tester expects. So I must admit I had practice making and fixing that error. My code, alternate searchers, and tester are here. Note that I fleshed out Bentley’s spec to my own liking.

    def binary_search( A, T ):
        """\
        Return i, nsteps.
        i is any int such that A[i] == T, else i = None
        nsteps is the number of steps it took.
        """
        imin, ilimit = 0, len(A)
        nsteps = 0
        while imin < ilimit:
            nsteps += 1
            i = imin + ( ilimit - imin ) / 2
            if A[i] == T:   return i, nsteps
    
            elif A[i] < T:  imin   = i + 1
            else:           ilimit = i
    
        return None, nsteps
    
    
    
    def linear_search( A, T ):
        """ Return i, nsteps
        i == first index such that A[i] == T, else i = None
        nsteps is the number of steps taken.
        """
        indexes = [ i for i in range( len(A) ) if A[i] == T ]
        if indexes: return indexes[ 0 ], len(A)
        else:       return None,         len(A)
    
    
  266. Matlab code:

    B = floor(rand(50,1)*100)
    T = 7
    A = sort(B)
    Found = 0

    while (length(A)>0)
    j = ceil(length(A)/2)
    if (A(j) > T)
    if (j ==1)
    A = [];
    else
    A = A(1:(j-1));
    end
    elseif (A(j)<T))
    if (j==length(A))
    A = [];
    else
    A = A((j+1):length(A))
    end
    else
    Found = 1;
    A = [];
    end
    end

    if (Found ==1)
    display('found the target number')
    else
    display('target number not found')
    end

    will test it in a second :)

  267. GO GO GADGET BINARY SEARCH FUNCTION:

    int binarySearch(int* array, int arraySize, int value)
    {
    int currentMax = arraySize-1;
    int currentMin = 0;
    int index;

    if( !array || arraySize <= 0 )
    return -1;

    while(1)
    {
    index = ((currentMax – currentMin) / 2) + currentMin;

    if( *(array+index) == value )
    return index;
    else
    {
    if( *(array+index) < value )
    {
    currentMin = index + 1;
    }
    else
    {
    currentMax = index – 1;
    }
    }

    if( currentMax < currentMin )
    return -1;
    }
    }

  268. A simple ruby script which take arguments from command line. The search is the first number, the following numbers are the array.

    #!/usr/bin/env ruby
    
    def rec_binsearch( tab, search, min, max )
        if min < max
            return -1
        end
        med=(max + min)/2
        if tab[med] == search
            return med
        end
        if tab[med] =0
        puts %{tab[#{res}]=#{tab[res]}}
    else
        puts %{can't find #{search} in [#{tab.join(' ')}]}
    end
    
  269. Okay, mine passes my tests!

    Testing binary_search
    
    Took 0 steps for 588 problems, smallest of size 0
    Took 1 steps for 538 problems, smallest of size 1
    Took 2 steps for 497 problems, smallest of size 2
    Took 3 steps for 461 problems, smallest of size 4
    Took 4 steps for 512 problems, smallest of size 8
    Took 5 steps for 489 problems, smallest of size 16
    Took 6 steps for 441 problems, smallest of size 32
    Took 7 steps for 383 problems, smallest of size 65
    Took 8 steps for 187 problems, smallest of size 128
    
    That was a test of binary_search
    
  270. I stupidly changed the name of the function after posting it to the comment box, but neglected to change the calls. Maybe that counts as failing?

    Anyways, here’s the correct version. Also, I just use default arguments instead of a helper function.

    from __future__ import division                                                                                                                                
                                                                                                                                                                   
    import math                                                                                                                                                    
                                                                                                                                                                   
    def binsearch(element, mylist, first_index=0):                                                                                                                 
        length = len(mylist)                                                                                                                                       
        if length == 0: return None                                                                                                                                
        if length == 1:                                                                                                                                            
            if mylist[0] == element: return first_index                                                                                                            
            else: return None                                                                                                                                      
        mid = int(math.floor(length / 2))                                                                                                                          
        if mylist[mid] == element: return mid + first_index                                                                                                        
        if element < mylist[mid]: return binsearch(element, mylist[0:mid], first_index)                                                                            
        return binsearch(element, mylist[mid:], first_index + mid)
    
  271. public class BinSearch {

    /**
    * Search for b in sorted (ascending) array a
    */
    public static int binSearch(int a[], int b) {
    if(a == null || a.length == 0)
    return -1;

    int min = 0, max = a.length-1;

    while(min <= max) {
    int mid = (min+max+1)/2;
    if(a[mid] b)
    max = mid;
    else
    return mid;
    }

    return -1;
    }
    }

  272. Here’s my attempt in Ruby. Haven’t tested it yet, but I’ll be doing that shortly.

    def binary_search(arr, obj, lower=0, upper=nil)
      upper = arr.length - 1 if upper.nil?
      
      pivot = (lower + upper) / 2
      return pivot if arr[pivot] == obj
    
      if upper - lower == 1
        return upper if arr[upper] == obj
        return nil
      end
      return nil if upper - lower == 0
    
      return binary_search(arr, obj, lower, pivot) if arr[pivot] >; obj
      return binary_search(arr, obj, pivot, upper) if arr[pivot] < obj
    end
    
  273. 
    Defends against gremlins that I can think of. No idea if zero length arrays are valid in C# but I don't want to find out :D ps, it compiles with a zero length array defined but unused.
    
    public static int search(int[] haystack, int needle)
            {
                int[] test = new int[0];
                int low = 0;
                int high = 0;
                if (haystack.Length > 0)
                {
                    high = haystack.Length - 1;
                }
                else
                {
                    return -1; //Can't find anything in an array with no entries.
                }
                int midpoint = 0;
    
                //If only searching smaller arrays (less than 10000 entries ish), feel free to skip this part.
                if (needle < haystack[low] || needle > haystack[high])
                {
                    return -1; //needle is smaller or larger than our values, no point searching.
                }
    
                while (low < high)
                {
                    midpoint = low + (high - low)/2;
                    if (needle > haystack[midpoint])
                    {
                        low = midpoint + 1;//Smallest possible is by definition at least one larger.
                    } 
                    else {
                        high = midpoint;//Largest it could possibly be is midpoint.
                    }
                }
                if (needle == haystack[midpoint])
                {
                    return midpoint;
                }
                else
                {
                    return -1; //Did not find needle in haystack.
                }
            }
    
  274. Jason Spencer

    Hey, sounds fun. I love a good challenge. I’m writing this in a text editor to avoid reading any previous comments that have come in since I read the article. Below is my implementation in Python 3 syntax (it “compiles” without errors). I have to say, it’s very hard not to test this before posting! I’m a bit of a novice, so I don’t expect this to be bug free. Go easy on me.

    #/usr/bin/env python3
    
    def binarySearch(search, list, middle=None, low=0, high=None):
    	if len(list) == 0:
    		return
    	if high == low:
    		return
    	if high == None:
    		high = len(list)
    	if middle == None:
    		middle = len(list)//2
    	if search == list[middle]:
    		return middle
    	if search > list[middle]:
    		# search top
    		low = middle
    		middle = (((high - middle) // 2) + middle)
    	else:
    		#search bottom
    		high = middle
    		middle = (((middle - low) // 2) + low)
    	return binarySearch(search, list, middle, low, high)
  275. public class BinSearch {
    	
    	/**
    	 * Search for b in sorted (ascending) array a
    	 */
    	public static int binSearch(int a[], int b) {
    		if(a == null || a.length == 0)
    			return -1;
    		
    		int min = 0, max = a.length-1;
    		
    		while(min &lt; max) {
    			int mid = (min+max)/2;
    			if(a[mid] <b> b)
    				max = mid;
    			else
    				return mid;
    		}
    		
    		return -1;
    	}
    }
    
  276. public class BinSearch {
    	
    	/**
    	 * Search for b in sorted (ascending) array a
    	 */
    	public static int binSearch(int a[], int b) {
    		if(a == null || a.length == 0)
    			return -1;
    		
    		int min = 0, max = a.length-1;
    		
    		while(min < max) {
    			int mid = (min+max)/2;
    			if(a[mid] < b)
    				min = mid + 1;
    			else if(a[mid] > b)
    				max = mid;
    			else
    				return mid;
    		}
    		
    		return -1;
    	}
    }
    
  277. Drew Benedetti

    I had to look up the syntax of ldiff, and initially forgot to floor mdx. That got it running, but it turns out I forgot to handle values that aren’t found.

    (defun bsearch (x range &optional (start 0))
      (let* ((mdx (floor (/ (length range) 2)))
             (mid (nth mdx range)))
        (cond ((= mid x) (+ start mdx))
             ((< mid x) (bsearch x (nthcdr (1+ mdx) range) (+ start mdx 1)))
             ((> mid x) (bsearch x (ldiff range (nthcdr mdx range)) start)))))
    

    I realize using nthcdr and ldiff is probably extremely terrible.

  278. bey0ndy0nder

    Just some quick thoughts….It’s given that the length is either odd or even. Thus, as one recurse it eventually all broil down to an Odd middle index or and Even middle index.

  279. Worked perfectly from the very start:

    #include <stdio.h>
    
    int binary_search(int * in, int length, int to_find) 
    {
      int left_bound = 0;
      int right_bound = length;
      while (1) {
        int current = left_bound + ((double) right_bound - left_bound)/2;
    
        if (in[current] == to_find) {
          return current;
        }
        if (left_bound >= right_bound) {
          return -1;
        }
        else if (in[current] < to_find) {
          left_bound = current + 1;
        } else if (in[current] > to_find) {
          right_bound = current - 1;
        }
      }
    
      return -1;
    }
    
  280. Untested… this was a good exercise!

    def bsearch(a, t):
      start = 0 
      end = len(a) - 1
      while	start &lt;= end:
        middle = (start + end) / 2
        if a[middle] == t:
          return middle
        elif a[middle] &lt; t:
          start = middle + 1
        else:
          end = middle - 1
      return -1
    
  281. Oops, it didn’t work on the empty array case. But other than that it worked.

  282. Untested… this was a good exercise!

    def bsearch(a, t):
      start = 0
      end = len(a) – 1
      while start <= end:
        middle = (start + end) / 2
        if a[middle] == t:
          return middle
        elif a[middle] < t:
          start = middle + 1
        else:
          end = middle – 1
      return -1
    
  283. Robert Græsdal

    ECMAScript implementation, using a small recursive function.

    I didn’t look at others source code, no looking at other binary search routines and no testing until it was done.

    This code makes a small dictionary tree and searches for words you put in.

    javascript:
    var tree = {
    	item:"m",
    	value:"Delicious candy!",
    	i0:{
    		item:"aardvark",
    		value:"Some kind of animal?"
    	},
    	i1:{
    		item:"roof",
    		value:"It keeps the pesky sun out",
    		i0:{
    			item:"protection",
    			value:"We're the mafia!"
    		},
    		i1:{
    			item:"zero",
    			value:"Definition: You."
    		}
    	}
    };
    
    function bsearch(T,N){
    	if( N == null)
    		return null;
    	else if(T > N.item)
    		return bsearch(T,N.i1);
    	else if(T < N.item)
    		return bsearch(T,N.i0);
    	else if(T == N.item)
    		return N;
    }
    
    alert( '"roof": '+bsearch("roof",tree).value );
  284. Jeremiah Via

    Some basic Haskell:

    search :: (Ord a) =&gt;  a -&gt; [a] -&gt; Maybe a
    search _ [] = Nothing
    search v xs 
        | v == middle xs = Just v
        | v   middle xs = search v (tail xs)
        where middle ys  = ys !! (length ys `div` 2)
    

    Matthias Goergens’ solution makes me realize that there is a lot of Haskell learning to do.

  285. failure!

  286. def binarysearch(lst, item, start=0, end=None):
        """returns None if it cannot find item in lst, otherwise returns the index"""
        if end is None:
            end = len(lst) #end is non-inclusive
    
        if end <= start:
            return None
    
        mid = start + (end - start) // 2
    
        if item < lst[mid]:
            return binarysearch(lst, item, start, mid)
        elif item > lst[mid]:
            return binarysearch(lst, item, mid+1, end)
        else:
            return mid
    
  287. Here is a VB.NET version that is tested. I failed a few cases on the first try. Biggest mistake was an off by one error. This is a rewrite to remove all the unnecessary code and fix all the edge cases.

        Function BinarySearch(ByVal a() As Integer, ByVal x As Integer) As Integer
    
            Dim min As Integer = 0
            Dim max As Integer = a.Length - 1
            Dim mid As Integer = 0
    
            While max >= min
                mid = min + (max - min) / 2
                If a(mid) = x Then
                    Return mid
                ElseIf a(mid) < x Then
                    min = mid + 1
                ElseIf a(mid) > x Then
                    max = mid - 1
                End If
            End While
    
            Return -1
    
        End Function
    
        Sub Main()
    
            Dim a() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
    
            For i As Integer = 0 To 21
                System.Console.WriteLine(String.Format("{0} is at {1}", i, BinarySearch(a, i)))
            Next
    
            System.Console.ReadLine()
    
        End Sub
    
    
  288. Tested, and seems to work. Got it first try.

    <?
    	function binary_search($needle,$haystack) {
    		$begin = 0;
    		$end = count($haystack) - 1;
    
    		if ($needle > $haystack[$end] || $needle < $haystack[$begin]) {
    			return false;
    		}
    		
    		$done = false;
    	
    		while(!$done) {
    			$split = ($end - $begin)/2 + $begin;
    
    			if ($haystack[$split] == $needle) {
    				$found = true;
    				$done = true;
    			} else if ($haystack[$split] > $needle) {
    				$end = $haystack[$split - 1];
    			} else if ($haystack[$split] < $needle) {
    				$begin = $haystack[$split + 1];
    			} else {
    				$done = true;
    				$found = false;
    			}
    		}
    		
    		return $found;
    	}
    ?>
    
  289. Testing is part of software development. This challenge is a bit like asking artists to draw a perfect circle while blindfolded. It’s a neat parlor trick, but you can’t use it as a litmus test for a “real artist” or “real programmer.”

    Anyhow; I saw that someone already posted an overly-generic C# IEnumerable solution, so I wasn’t going to post mine. But since the earlier solution uses the ElementAt extension method, mine is still notable in the “overkill” category.

          static T bsearch<T>(T wanted, IEnumerator<T> iter, int count) where T : IComparable
          {
             if (count <= 0 || iter.Current == null)
                return default(T);
    
             if (count == 1)
                if (wanted.CompareTo(iter.Current) == 0)
                   return iter.Current;
                else
                   return default(T);
    
             List<T> seen = new List<T>();
             for (int ii = 0; ii < count / 2; ii++)
             {
                seen.Add(iter.Current);
                if (!iter.MoveNext())
                   return default(T);
             }
    
             int result = wanted.CompareTo(iter.Current);
             if (result == -1)
                return bsearch(wanted, seen.GetEnumerator(), count / 2);
             else if (result == 0)
                return iter.Current;
             else
                return iter.MoveNext() ? bsearch(wanted, iter, count - (count / 2) - 1) : default(T);
          }
    
  290. public class BinarySearch {
    	public static <T extends Comparable<? super T>> int search(final T value, final T[] values) {
    		int index = -1;
    		int low = 0;
    		int high = values.length;
    		int mid = -1;
    		
    		while (index < 0) {
    			mid = (low + high)/2;
    			if (values[mid].compareTo(value) > 0) {
    				if (high == mid) {
    					break;
    				} else {
    					high = mid;
    				}
    			} else if (values[mid].compareTo(value) < 0) {
    				if (low == mid) {
    					break;
    				} else {
    					low = mid;
    				}
    			} else {
    				index = mid;
    			}
    		}
    		
    		return index;
    	}
    }

    untested, java

  291. ok, so after about 25 minutes,

    basic psuedo code is

    return index if good
    return go_left(lower, index-1) if less good
    else return go_right(index+1, upper)

    I have not tested the following code…

    def bsearch(lower, upper, value, arr):
      index = int((lower+upper)/2)
      if  (arr[index] == value):
        return index
      if (arr[index] < value):
        return bsearch(lower,index-1,value,arr)
      else:
        return bsearch(index+1,upper,value,arr)
    
    main(low_to_high_sorted_arr, value):
      return bsearch(0, len(arr)-1, value, arr)
    
    

    10 minutes later the above is typed in.

    Looks like more specific psudocode, but is hopefully well formed python.

    This assumes either that the value is unique, or that we don’t care which index is returned for multiple.

    Mind you I’ve done this in school previously, so it was good reminder.

    If I had more time I’d step through with a couple of cases. Maybe even test it in an actual interpreter.

  292. Here’s my attempt at a recursive D (D 2.0) version:


    /**
    * An implementation of the binary search algorithm for
    * https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    */
    module bsearch;
    /**
    * Performs a binary search over list 'l' for element 'e'.
    * Returns the index, or -1 on failure.
    *
    * 'base' is for internal use by the function.
    *
    * The return value should be size_t, but we are allowed to ignore overflow.
    */
    int bsearch(T)(T[] l, T e, int base = 0)
    {
    int index(int i)
    {
    return base + i;
    }
    if (l.length == 0) return 1;
    if (l.length == 1) {
    if (l[0] == e) {
    return index(0);
    } else {
    return 1;
    }
    }
    size_t mid = l.length / 2;
    if (e == l[mid]) {
    return index(mid);
    } else if (e < l[mid]) {
    return bsearch(l[0 .. mid], e, 0 + base);
    } else if (e > l[mid]) {
    return bsearch(l[mid .. $], e, mid + base);
    }
    // We shouldn't reach here.
    assert(false);
    }
    void main()
    {
    }
    unittest
    {
    int[] l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    assert(bsearch(l, 5) == 4);
    assert(bsearch(l, 1) == 0);
    assert(bsearch(l, 10) == 9);
    assert(bsearch(l, 11) == 1);
    assert(bsearch(l, 0) == 1);
    int[] ll = [5];
    assert(bsearch(ll, 5) == 0);
    assert(bsearch(ll, 6) == 1);
    assert(bsearch(ll, 4) == 1);
    ll ~= 6;
    assert(bsearch(ll, 5) == 0);
    assert(bsearch(ll, 6) == 1);
    assert(bsearch(ll, 7) == 1);
    assert(bsearch(ll, 4) == 1);
    int[] z = [];
    assert(bsearch(z, 1) == 1);
    }

    view raw

    bsearch.d

    hosted with ❤ by GitHub

    /**
     * Performs a binary search over list 'l' for element 'e'.
     * Returns the index, or -1 on failure.
     *
     * 'base' is for internal use by the function.
     *
     * The return value should be size_t, but we are allowed to ignore overflow.
     */
    int bsearch(T)(T[] l, T e, int base = 0)
    {
        int index(int i)
        {
            return base + i;
        }
    
        if (l.length == 0) return -1;
        if (l.length == 1) {
            if (l[0] == e) {
                return index(0);
            } else {
                return -1;
            }
        }
    
        size_t mid = l.length / 2;
        if (e == l[mid]) {
            return index(mid);
        } else if (e < l[mid]) {
            return bsearch(l[0 .. mid], e, 0 + base);
        } else if (e > l[mid]) {
            return bsearch(l[mid .. $], e, mid + base);
        }
    
        // We shouldn't reach here.
        assert(false);
    }
    

    It passes my tests (see the gist) but I’m sure I’ve missed an edge condition or four.

  293. def bsearch(needle, haystack, lo, hi):
        if hi - lo == 0:
            return False
        elif hi - lo <= 1:
            return haystack[lo] == needle
    
        mid = (lo + hi) // 2
    
        if haystack[mid] == needle:
            return True
        elif haystack[mid] < needle:
            return bsearch(needle, haystack, mid, hi)
        else:
            return bsearch(needle, haystack, lo, mid)
    
    import random
    arr = [ random.randint(1,100) for _ in range(100) ]
    arr.sort()
    
    for e in range(100):
        assert (e in arr) == bsearch(e, arr, 0, 100)
    
  294. Can someone test mine? I’m too lazy to. -.-

  295. class MyArray  range_end
    
        middle = ((range_end - range_start) / 2) + range_start
    
        return -1 if self[middle].nil?
    
        if needle > self[middle] 
          index_of_recurse(needle, middle + 1, range_end)
        elsif needle < self[middle]
          index_of_recurse(needle, range_start, middle - 1)
        else
          return middle
        end
      end
    end
    

    Success–but I did it recursively, which I consider kind of a fail given the use case.

  296. Robert Græsdal

    I notice I’ve misunderstood the task. I should have worked on an array and not a tree structure.

  297. #!/usr/bin/perl
    use POSIX qw(floor);
    
    sub binSearch($\@)
    {
        my $value = shift;
        my @data = @{(shift)};
    
        return ($value == $data[0])
            if(0 == $#data);
    
        my $cutPoint = 
            floor(($#data+1)/2.0);
    
        if($value < $data[$cutPoint])
        {
            my @lft = splice(@data,0,$cutPoint);
            return binSearch($value,\@lft);
        }
    
        my @rgt = splice(@data,$cutPoint);
        return binSearch($value,\@rgt);
    }
    
    my @data = (1,19,29,38,38,38,41,42,59,199);
    
    if(binSearch(42,@data))
    {
        print "found\n";
    }
    else
    {
        print "unfound\n";
    }
    
  298. I’m going to do the “extra credit” macho option and post my solution before testing it. (I did run a syntax check, and I did mentally trace through a number of cases first.)

    ;;; Searches vector v for element e with the less-than procedure lt,
    ;;; returning its index if found, else #f
    (define (binary-search v e (lt string<?))
      (define (search a c)
        (if (= a c) #f
            (let* ((b (truncate (/ (+ a c) 2)))
                   (m (vector-ref v b)))
              (cond
               ((lt e m) (search a b))
               ((lt m e) (search (+ b 1) c))
               (else b)))))
      (search 0 (vector-length v)))
    
  299. int search(int v, int[] p)  {
           int i = 0;
           int j = p.size;
           while (j - i)  {
                int n = (j-i) / 2 + i;
                if (p[n] > v)
                       i = n + 1;
                else 
                      j = n;
           }
           return (p[i] == v) ? i : -1;
    }
    

    I wrote this from memory – but as I wrote an assembler version only a few weeks ago, the technique is still fresh in my mind – and specifically the use of array.size as the upper bound, rather than array.size-1.

    Scanning through the other comments, seems like this one is very similar.

  300. I failed – simple bug, but failure none the less.

    However, I don’t really see a point in this exercise – are you(or the author of the book) implying that if a programmer can’t implement this on first try, without testing, he’s not as good as one who can?

    IMO, it’s not a good metric for the ability of the programmer.

    Now, if a programmer doesn’t _understand_ the binary search algorithm after reading it – then we might have a problem…

  301. Tested and seems to work in ruby, looking for the bug after posting comment:

    max_range = rand(1000)
    range = [1...max_range]
    T = rand(max_range)
    
    puts "mac_range: #{max_range}"
    puts "rand T = #{T}"
    
    while(range.empty?)
    	T = ceil(range.size / 2)
    	if T == range[T]
    		break
    	elsif T < range[T]
    		range[1...T]
    	else
    		range[T...max_range]
    	end
    end
    
    puts "T = #{T}";
    
  302. public class BinSearch {
    	public static void main(String[] args) {
    		int[] arg = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    		System.out.println(binSearch(arg, 5));
    		System.out.println(binSearch(arg, 0));
    		System.out.println(binSearch(arg, 8));
    		System.out.println(binSearch(arg, 10));
    		System.out.println(binSearch(arg, -1));
    		System.out.println(binSearch(arg, 11));
    		
    		for(int i = 0; i <= 9; i++) {
    			System.out.println(binSearch(arg, i));
    			if(i != binSearch(arg, i))
    				System.out.println("Failed test!");
    		}
    	}
    	
    	public static int binSearch(int[] arr, int n) {
    		if(arr == null) 
    			throw new NoSuchElementException();
    		
    		int high = arr.length-1;
    		int low = 0;
    		
    		int tmp;
    		while(true) {
    			tmp = arr[(high-low)/2 + low];
    			// If tmp is less than n, set params to second half
    			if(tmp < n) {
    				low = ((high-low)/2 + low)+1;
    			}
    			// If tmp is greater than n, set params to first half
    			else if(tmp > n) {
    				high = ((high-low)/2 + low)-1;
    			}
    			// if tmp is n, return true
    			else {
    				return tmp;
    			}
    			
    			// if the bounds are less than 4, save us all a headache and just seqsearch it
    			if(high-low <= 4) {
    				for(int i = low; i <= high; i++) {
    					if(arr[i] == n)
    						return i;
    				}
    				return -1;
    			}
    		}
    	}

    I think I passed, but I didn’t test.

    Question, though: if we all know that only 10% of people passed this test, won’t we also try harder, thus skewing the results? A balanced experiment would be double-blind.

  303. Thoroughly. I mean I didn’t test thoroughly. Just FYI.

  304. Only problem I had when I did my first test was that I forgot to return the results of my recursive call to binary_search.

    import math
    
    def binary_search(target, value, size=None):
        if not size: size = len(target)
        half_size = int(math.floor(size / 2))
        mid = target[half_size - 1]
        if mid > value: target = target[:half_size]
        elif mid < value: target = target[half_size:]
        else: return half_size - 1
        return binary_search(target, value, half_size)
    
    test = [1,3,4,7,9,10,22,35,67,365,1000,1024,2010,3000,9000]
    find = 22
    pos = binary_search(test, find)
    print test[pos] == find
    
  305. def binary_search(needle, haystack):
        def recurse(begin, end):
            pivot = int((end - begin) / 2) + begin
            pivot_value = haystack[pivot]
            if needle  pivot_value:
                return recurse(pivot + 1, end)
            return pivot
        return recurse(0, len(haystack))
    

    Test suite:

    import random
    samples = []
    for i in range(0, 1000):
        data = set()
        count = random.randint(1, 10000)
        for j in range(0, count):
            data.add(random.randint(-10000, 10000))
        data = list(data)
        data.sort()
        needle = random.choice(data)
        
        samples.append(dict(needle=needle, data=data))
    
    for sample in samples:
        correct = sample['data'].index(sample['needle'])
        found = binary_search(sample['needle'], sample['data'])
        if found == correct:
            print 'Success'
        else:
            print 'Failed on needle: %d. Found %d, should have been %d.' % (sample['needle'], found, correct)
    
  306. Frans-Willem Hardijzer

    C++ code, not tested or compiled, returns either an index or -1 for not found. Pretty confident it’ll work, let me know if it doesn’t ;)

    int bsearch(int *arr, int len, int find) {
    	int left=0; //First element in range
    	int right=len; //Just past last element in range
    	while (right>left) { //Range is non-empty as long as right&gt;left
    		int mid=(left+right)/2; //Rounded down automatically
    		if (arr[mid]>find) {
    			// mid point is past value to be found, so range is below this
    			right=mid;
    		} else if (arr[mid]<find) {
    			// mid point is before value to be find, so range starts after this
    			left=mid+1;
    		} else {
    			// thus arr[mid]==find
    			return mid;
    		}
    	}
    	return -1;
    }
    
  307. When I read “The only way you’ll believe this is by putting down this column right now and writing the code yourself. Try it.” I stopped reading and tried it.

    My binary searches worked without error. Although the first test case I wrote made me think it didn’t because it passed in an unsorted array – and the item wasn’t found.

    I think the lack of testing is bunk – who writes code without testing it? We need more people that write correct code because they’ve tested it fully. Not more people who submit code without testing because they think they’re one of the 10% who can do it.

    Disclaimer:
    I stole many of the tests by reading the comments.

    
    def binary_search(array, char):
        start = 0
        stop = len(array)-1
        not_found = True
        while not_found and start <= stop:
            middle = ((stop - start) / 2) + start
            mid_char = array[middle]
            if mid_char == char:
                return middle
            elif char < mid_char:
                stop = middle-1
            elif char > mid_char:
                start = middle + 1
        return -1
    
    import string
    character_array = list(string.letters)
    character_array.sort()
    
    print "found"
    print binary_search(character_array, 'B')
    print binary_search(character_array, 'k')
    print binary_search(character_array, 'z')
    print binary_search(character_array, 'A')
    print binary_search(character_array, 'Z')
    print binary_search(range(10), 0)
    print binary_search(range(10), 1)
    print binary_search(range(10), 2)
    print binary_search(range(10), 9)
    
    print "not found"
    print binary_search(character_array, '%')
    print binary_search(character_array, '(')
    print binary_search(character_array, '(')
    print binary_search(range(10), 10)
    print binary_search([], 10)
    print binary_search(range(10), "a")
    print binary_search(character_array, character_array)
    
  308. Ps. I just realized that the not_found variable is redundant – but it doesn’t hurt anything aside from memory usage.

    :P

  309. Code plus tests. Code in less than 5 minutes + another 10 to write the tests and be sure that it did what it should do before running it.

    I understand that one can slip a bug into something that is taken for granted, but come on…

    def bsearch(orderedlist, value):
        if len(orderedlist) == 0:
            return False
        middle = len(orderedlist) / 2
        mvalue = orderedlist[middle]
        if mvalue == value:
            return True
        elif mvalue < value:
            return bsearch(orderedlist[middle+1:], value)
        else:
            return bsearch(orderedlist[:middle], value)
    
    # datasets
    even = (1, 3, 5, 7, 9, 11)     # Even number of items
    odd  = (1, 3, 5, 7, 9, 11, 13) # Odd number of items
    not_there = (2, 4, 6, 8, 10, 12)
    
    tests = ( # ( haystack, needle(s), expected answer)
              ( even, 0, False ),  # Looking for values out of the range
              ( even, 12, False ),
              ( odd, 0, False ),
              ( odd, 0, False ),
    
              ( even, not_there[:-1], False ), # Looking for values in the range,
              ( odd, not_there, False ),       # but not in the dataset
    
              ( even, even, True ),            # Looking for the values in the
              ( odd, odd, True ),               # datasets
            )
    
    for haystack, needles, expected in tests:
        if not isinstance(needles, tuple):
            needles = (needles,)
        for needle in needles:
            if bsearch(haystack, needle) == expected:
                print "PASSED: %d in %s?" % (needle, haystack)
    
  310. Jason Spencer

    Mine (posted above) seems to work. Since we were ignoring numeric overflow I went ahead and assumed we could ignore stack overflow as well. For a real library or to run on an embedded device (I like AVRs) I’d write an explicitly iterative version.

    #/usr/bin/env python3
    
    def binarySearch(search, list, middle=None, low=0, high=None):
    	if len(list) == 0:
    		return
    	if high == low:
    		return
    	if high == None:
    		high = len(list)
    	if middle == None:
    		middle = len(list)//2
    	if search == list[middle]:
    		return middle
    	if search > list[middle]:
    		# search top
    		low = middle
    		middle = (((high - middle) // 2) + middle)
    	else:
    		#search bottom
    		high = middle
    		middle = (((middle - low) // 2) + low)
    	return binarySearch(search, list, middle, low, high)
    
    print(binarySearch(0, []))
    print(binarySearch(1, [1]))
    print(binarySearch(2, [2,4,6,12]))
    print(binarySearch(4, [2,4,6,12]))
    print(binarySearch(6, [2,4,6,12]))
    print(binarySearch(12, [2,4,6,12]))
    print(binarySearch(2, [2,4,6,12,13]))
    print(binarySearch(4, [2,4,6,12,13]))
    print(binarySearch(6, [2,4,6,12,13]))
    print(binarySearch(12, [2,4,6,12,13]))
    print(binarySearch(13, [2,4,6,12,13]))
    
    ## Output:
    # None
    # 0
    # 0
    # 1
    # 2
    # 3
    # 0
    # 1
    # 2
    # 3
    # 4
    

    Does anyone have any tests I haven’t thought of?

  311. Given the blog owner’s choice of Lisp language, I’m surprised that of the first ~300 entries, mine was the only Scheme one. Wow! :-) (I just grepped for “(define”, so someone correct me if I missed something.)

  312. Ruby – NON Recursive

    So perhaps it doesn’t count having written once already… but I wrote a new one, avoided the syntax errors that caught me up the first time, realized that recursion is going to eat a lot of unnecessary resources for large arrays, and ran correctly out of the box this time. :)

    def binsort(target,sorted_array)
      @left = 0;
      @right = sorted_array.length - 1
      @result = -1
      while (@result<0)
        @range = @right - @left
        if (@range<0)
          break
        end
        @index = @left + ((@range%2==0) ? @range/2 : (@range+1)/2)
        if (target==sorted_array[@index])
          @result = @index
        elsif (target>sorted_array[@index])
          @left = @index+1
        else
          @right = @index-1
        end
      end
      return @result
    end
    

    It’s interesting that all the common error cases (null array, element missing) get caught by the same test for @range being negative.

  313. Untested, un-peeked (except for the non-overflowing mid calculation which was in one of the first solutions). I already know it doesn’t do the empty array test so, I fail it but, let hope the rest works…

    int binary_search(int target, int* ints, unsigned n)
    {
       // check range first (too dumb to try to incorporate it into the main algorithm)
       if (target < ints[0] or ints[n-1] < target) {
          return -1;
       }
    
       // hi is one-past-end
       for (int lo = 0, hi = n; lo < hi; ) {
          // yah bug I read that blog post recently
    //      int mid = (lo + hi) / 2;
          // correction that I cheated and looked at
          int mid = lo + (hi - lo) / 2;
          int found = ints[mid];
    
          if (target < found) {
             hi = found;
          }
          else if (found < target) {
             lo = found;
          }
          else {
             return mid;
          }
       }
       // failure
       return -1;
    }
    
    
  314. Err… forgot to post the couple last lines :P

            else:
                print "ERROR:  %d in %s?" % (needle, haystack)
    
  315. Forgot to account for a search for a non-existent element; so I failed :(

    function binSearch($A,$value) {
    	$range = $idx = ceil(count($A) / 2);
    	while ($range > 0) {
    		if ($A[$range] == $value) return $range;
    		$idx = ceil($idx / 2);
    		if ($A[$range] < $value) {
    			$range += $idx;
    		} else {
    			$range -= $idx;
    		}
    	}
    	return -1;
    }
    
  316. Works as far as I can tell…

    #!/Users/jsharpe/.rvm/rubies/ree-1.8.7-2010.01/bin/ruby
    
    # $ ./binary.rb 10
    # not found
    # $ ./binary.rb 12
    # 12
    
    class Array
      def bsearch(n)
        if self.empty?
          return "not found"
        else
          i = self.length / 2
          midpoint = self[i]
          if n == midpoint
            return n
          elsif n < midpoint
            new_end = self.length == 1 ? -2 : i - 1
            self[0 .. new_end].bsearch(n)
          elsif n > midpoint
            self[i + 1 .. -1].bsearch(n)
          end
        end
      end
    end
    
    arr = Array(-500..500).map{|a| a * 3}
    
    puts arr.bsearch(ARGV[0].to_i)
    
  317. This is interesting, a somewhat related post can be found here:
    http://googleresearch.blogspot.com/2006/06/extra-extra-read-
    The gist of this is that version of BSearch implemented in the JDK contained a bug due to an overflow error. I recall reading about it some time ago, funny to see it come up again.

    [Mike says: this is, I think, the third comment point out the Josh Bloch integer-overflow article as though I hadn’t linked to it from the original post itself. I can only assume these are being posted by people who’ve not actually read my post. I’m letting them all through moderation because they’re not spam and not abusive, but they really don’t add much to the discussion.]

  318. uy, lo,hi = found instead of mid; IFI. But other than that mine’s good. Bonus points for elegance I say; not a +/- 1 to be found :)

  319. I didn’t read the challenge until I tested my code. I only made one change after testing, and that did not affect correctness. (I had a special case for arrays of length 1.)

    It turns out I instinctively avoided a bug that would have resulted in an infinite loop when checking a slice containing two elements. Lucky, I say.

    Also in D.

    int find_impl(T)(T[] arr, T val, size_t index)
    {
        if (arr.length == 0) return -1;
        auto center = arr.length / 2;
        if (arr[center] == val) return center + index;
        if (arr[center] < val) return find_impl(arr[center + 1 .. $], val, index + center + 1);
        return find_impl(arr[0 .. center], val, index);
    }
    
    int find(T)(T[] arr, T val)
    {
        return find_impl(arr, val, 0);
    }
    
  320. First version had a bug in it, easily fixed though.

    Looking through the comments I like the recursive solutions.

    Does it mean you’re not a professional or decent programmer if your first version has a couple of bugs in it…? i don’t think so.

    I’m not sure what this proves.

  321. I think this is a bit of a sham. My guess is the author of the book is just overly picky when reviewing. My first run seg faulted :P but this was not a result of the function, but rather an argument reading error where I tried to read argv at argc instead of argc-1, so I don’t count that as my logic error as it was outside the bsearch function. All subsequent testing succeeded.

    int bs(int size, int *array, int needle)
    {
            int ub=size-1;
            int lb=0;
            int point=0;
    
    
            while (lb!=ub)
            {
                    point=(ub+lb)/2;
                    if (needle==array[point])
                            return (point);
                    else if (needle>array[point])
                            lb=point+1;
                    else if (needle<array[point])
                            ub=point-1;
            }
    
            if (array[lb]==needle)
                    return (lb);
            else
                    return (-1);
    }
    
  322. @Joe User (comment 1805): Applied my tests to your code and it failed in the first one, ie. looking for a value outside the range. Say you have a list [1, 3, 5, 7] and I look for 0… your code gets into an infinite loop

  323. private static boolean search(int[] array, int start, int end, int t) {
    		if (end - start == 1) {
    			return array[start] == t || array[end] == t;
    		}
    		if (end == start) {
    			return (array[start] == t);
    		}
    		int middle = (end-start)/2+start;
    		middle++;
    		if (array[middle] == t) {
    			return true;
    		}
    		else if (array[middle]< t) {
    			return search(array, middle+1, end, t);
    		}
    		else {
    			return search(array, start, middle-1, t);
    		}
    	}
    

    Works AFAIK

  324. One last attempt to get word press to quit eating all my code :(

    import org.junit.Test;
    import static org.junit.Assert.*;
    
    public class BinarySearch {
       
        public int binarySearch(int x, int[] array) {
            int low = 0;
            int high = array.length - 1;
            int i = (high+low) / 2;
            
            while (low < high && array[i] != x) {
                if (array[i] > x) {
                    high = i - 1;
                    i = (high+low) / 2;
                } else if  (array[i] < x) {
                    low = i + 1;
                    i = (high+low) / 2;                
                }
            }
            
            if (array[i] == x)
                return i;
            return -1;
        }
    
        @Test
        public void test() {
            int [] array = new int[] {0,1,2,3,4,5,6,7,8,9 };
            
            for (int i = 0; i < array.length; i++) {
                assertEquals(i, binarySearch(i, array));
            }
            assertEquals(-1, binarySearch(20, array));
            assertEquals(-1, binarySearch(-20, array));
        }
    }
    
  325. I know I just posted, but some of these implementations (assuming they passed testing) might be useful over at http://rosettacode.org/wiki/Binary_search if your language hasn’t been done yet.

  326. Mine worked.

    I created a binary tree (computing next node pointer during the search, not ahead of time). It uses recursion. It’s probably not the fastest, and with 3 classes it’s also the most lines that i’ve seen so far… but it works.

    I did it in C++.

  327. I failed, but I got it fixed in about 5 mins :)

  328. Test cases are key. My first implementation worked for all values in the list, and values that fell inside the range of the list (> than least, greatest and < least test cases popped into my head. Had to add another check for the latter. So, does it count as a fail if you yell "Done!" then realize you missed something?

  329. Ruby implementation came close. I’m pretty proud of that given that it was my first run ever, with no formal compsci training. Took three edits to match my big test suite :)

    http://pastebin.org/160181

  330. Though the do-without-testing concept is interesting, I’m not really sure it accurately represents programming prowess. If an application is being correctly designed, automated testing while working should be encouraged.

    I *could* have spent half an hour poring over the code, running test cases myself, but instead I just ran the test suite, found the problem, and identified and fixed it within a minute. Writing an algorithm in one’s head, pass/fail, seems like an interesting test, but it not the mark of a good, productive programmer.

    Or maybe I’m just whining since my 5-minute attempt didn’t pass. Ah well.

  331. Writing code without testing is akin to doing math without a calculator.

  332. This seems to work – first attempt.

    # the list to be searched
    data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
    
    # what we're searching for
    search = 7
    
    found = False
    while not found:
        # did we run out of data?
        if len(data) == 0: break
    
        # partition the list (take advantage of default integer division in 2.6)
        p = len(data) / 2
    
        # proceed based on data at partition
        if data[p] == search:
            found = True
            break
        elif data[p] > search:
            data = data[:p]
        else:
            data = data[p+1:]
    
    if found:
        print "Found it!"
    else:
        print "Not found."
    
  333. Got mine working (as much as I can see, but that doesn’t say much…) after one quick bug in testing (instead the correct array position for the found (pos[mid]) i returned mid)… Any comments?

    def bsearch(inlist, item):
        n = len(inlist)
        if n == 0:
            return None
        pos = range(n)
        while len(pos) > 0:
            mid = int(len(pos) / 2.0)
            test = inlist[pos[mid]]
            if (test == item):
                return pos[mid]
            elif (test < item) and (mid  item) and (mid > 0):
                pos = pos[0:mid]
            else:
                return None
        return None
    
    [1]: mylist = ['a', 'b', 'c', 'd']
    [2]: print bsearch(mylist, 'c')
    > 2
    
  334. It won’t pass the integer overflow test (though at least it will throw an exception rather than silently failing), but here’s my answer in Clojure:

    (defn binary-search
      "Performs a binary search for a value v in a given sequence s.
       Returns an index of the value if found, nil otherwise."
    
      ([s v a b]
         (if (> a b)
           nil
           (let [m (floor (/ (+ a b) 2))
                 x (s m)]
             (cond (= v x) m
                   (> v x) (recur s v (inc m) b)
                   (< v x) (recur s v a (dec m))))))
    
      ([s v]
         (binary-search s v 0 (dec (count s)))))
    

    So that’s my “vote” — if we were supposed to protect against integer overflow I didn’t get it, otherwise I did.

  335. “””
    def bsearch(x, l):
    print l
    if len(l) == 1:
    return x == l[0]
    pivot = len(l)/2
    val = l[pivot]
    if x == val:
    return True
    elif x < val:
    return bsearch(x, l[:pivot-1])
    else:
    return bsearch(x, l[pivot:])
    """

  336. Matthias Goergens

    Jonathan Deutsch, I can’t decide whether your comment is trite or deep.

    Arithmetic without a calculator borders on nonsense. But for analysis, algebra or proofs in general, you’d need a very advanced calculator.

  337. Succeeded (at least on my test cases), in M. I assume array is zero-indexed.

    find(array,val) new pos,delta,found
      set delta=array,pos=array\2
      for  set delta=delta\2 do  quit:found  quit:delta<1  
      . if pos>array set pos=pos-delta quit
      . if pos<0 set pos=pos+delta quit
      . if array(pos)>val set pos=pos-delta quit
      . if array(pos)<val set pos=pos+delta quit
      . if array(pos)=val set found=1 quit
      quit $select(found:pos,1:-1)
    

    (It may be worth noting that the right way to do this in M would be to use the values as the subscripts to the array, since arrays in M are actually more of a map structure.)

  338. Hmm. So the question now is, can we trust programmers to tell the truth about how good they are?

    I think the answer is clearly no.

  339. Success under the specified conditions, but a slight failure in the enhancement I tried to make at the same time.

    I tried to get the .NET semantics where the index is returned when the element is found, and the two’s complement of the index the element should have been otherwise. I rushed the two’s complement part and got the value slightly wrong, but the basic ‘is-it-there-or-not’ search was correct, including overflow safety provided my ‘a + ((b – a)/2)’ is the correct solution for a safe midpoint calculation… I didn’t have enough memory to determine whether there is a gotcha there ;)

  340. Mine seems to work, at least for the handful of testcases I tried.

    char* search(char x, char a[], int n) {
      if (n==0)
          return NULL;
    
      int mid = n/2;
      if (x == a[mid])
          return &a[mid];
      else if (x < a[mid])
          return search(x, a, mid);
      else
          return search(x, a+mid+1, n-mid-1);
    }
    
  341. Michael Schmahl

    As requested, I report that I failed. I wrote

    if (array[test] > value) low = test+1;
    

    When it should have been:

    if (value > array[test]) low = test+1;
    
  342. class Array:
        data = []
        def __init__(self, arr=[]):
            self.data = arr[:]
            
        def search(self, e, start=0, end=None):
            if end==None: end=len(self.data)
    
            if end <= start: return None
            if start < 0: return None               # really throw
            if end > len(self.data): return None    # really throw
    
            mid = start + (end-start)//2
    
            if self.data[mid] == e: return mid
            if self.data[mid] < e: return self.search(e, mid+1, end)
            return self.search(e, start, mid)
    

    Tested briefly, seems to work correctly. Assuming I can use the rule “You’re allowed to use your compiler to shake out mechanical bugs such as syntax errors or failure to initialise variables” to cover learning to write classes in Python (because it took me three tries to reference class variables correctly), it worked first try.

  343. Wrote, tested, posted. Note that this is searching for a key-value pair with a given key.

    def search_helper(l, key, mini, maxi):
        midi = (mini+maxi)/2
        if midi == mini or midi == maxi:
            return None
        kv = l[midi]
        if kv[0] == key:
            return kv
        elif kv[0] < key:
            return search_helper(l,key,midi,maxi)
        else:
            return search_helper(l,key,mini,midi)
    
    def bsearch(l,key):
        return search_helper(l,key,-1,len(l))
    
  344. How about proving it correct before even testing? I’ve used VCC http://vcc.codeplex.com/

    #include <vcc.h>
    #include <limits.h>
    
    int bs(unsigned int len, int *a, int t)
    requires (len >= 0)
    requires (len < UINT_MAX/2)
    requires (wrapped(as_array(a, len)))
    requires (forall(int i; 0 < i && i < (int)len ==> forall (int j; 0 <= j && j < i ==> a[j] <= a[i])))
    ensures (result >= 0 ==> a[result] == t)
    ensures (result < 0 ==> forall(int i; 0 <= i && i < (int)len ==> a[i] != t))
    {
        unsigned int start = 0, end = len;
        while (start < end)
    	invariant (end <= len)
    	invariant (forall(int i; 0 <= i && i < (int)start ==> a[i] < t))
    	invariant (forall (int i; (int)end <= i && i < (int)len ==> a[i] > t))
    	{
            unsigned int m = (start + end) / 2;
            if (a[m] < t) start = m + 1;
            else if (a[m] > t) end = m;
            else return (int)m;
        }
        return -1;
    }
    
  345. Matthew Todd

    To say only 10% can write a binary search is inaccurate. The rules being applied here are not realistic and rather contradictory to some development processes. For example:

    NO testing until done writing? What about Test Driven Development?

    Do you actually expect programmers to be able to write completely bug free code on their first run through?

    I understand the basic premise: that programmers seemingly aren’t as good as we’d expect. But this feels like another apocalyptic assessment of how “kids” these days can’t code worth a damn. Not to say that all the high level coding with libraries degrading overall competency isn’t a worry.

    I should mention that I fall in the kid age group, as I’m still in college. But I’m nearing the teenage years regarding overall experience and intuition.

  346. To be honest I only tested it with one array, I spent most of my time making it small… I was hoping to get it in under 80 characters, but I’ve only managed 111.

    int b(int v,int d[],int l){int i,n=l>>=1;do{i=n;n=v<d[i]?i-l:v>d[i]?i+l:i;l>>=1;l=l?l:1;}while(n!=i);return i;}
  347. Got this wrong on the first try, didn’t handle failure to find the target item. Can’t find any other bugs but let me know if you do:

    function bsearch(arr, target) {
      var rec = (function(s,e) {
        if(s > e) return null;
        var idx = Math.floor((e-s)/2) + s;
        if(arr[idx] == target) {
          return idx;
        } else if(target < arr[idx]) {
          return rec(s,idx-1);
        } else {
          return rec(idx+1, e);
        }
      });
      return rec(0, arr.length-1);
    }
    
  348. In Go:

    func bsearch(s []int, t int) int {
    	var i int
    	for {
    		if len(s) == 0 {
    			return -1
    		}
    		i = len(s)/2
    		if s[i] == t {
    			return i
    		} else if s[i] > t {
    			s = s[0:i]
    		} else {
    			s = s[i+1:]
    		}
    	}
    	return -1
    }
    
  349. def bsearch(ar, v):
    if len(ar)==0: return -1
    if v ar[-1]: return -1
    return bsint(ar, v, 0, len(ar))

    def bsint(ar, v, f, t):
    if f==t:
    if ar[f]==v:
    return f
    else:
    return -1
    mid = (f+t)/2 # integer arithmetic
    am = ar[mid]
    if am==v: return mid
    if vam: return bsint(ar, v, mid+1, t)

  350. Wrong the first time.

  351. Had less-than rather than less-than-or-equal-to as my loop-condition but other than that it worked in my tests.

    /*
     * Returns index of element with value item if present, -1 otherwise.
     */
    static int my_bsearch(const int *pArray, int size, int item)
    {
        int start_point = 0;
        int end_point = size - 1;
        int mid_point;
    
        do
        {
            mid_point = start_point + ((end_point - start_point) / 2);
    
            if (pArray[mid_point] == item)
            {
                return mid_point;
            }
            else if (pArray[mid_point] < item)
            {
                start_point = mid_point + 1;
            }
            else
            {
                end_point = mid_point - 1;
            }
        } while (start_point <= end_point);
    
        return -1;
    }
    
  352. Correct after fixing two syntax errors:

    def binSearch(lst,value):
        return binSearchR(lst,value,0,len(lst)-1)
    
    def binSearchR(lst,value,minimum,maximum):
        if maximum-minimum == 0:
            if lst[maximum] == value:
                return maximum
            else:
                return -1
        middle = minimum+(maximum-minimum)/2
        if lst[middle] == value:
            return middle
        elif lst[middle] > value:
            return binSearchR(lst,value,minimum,middle-1)
        else:
            return binSearchR(lst,value,middle+1,maximum)
        
    #testing...
    print binSearch([0,2,4,6,8,10,11,14,29,35,46,98],35)
    
  353. forgot to mention that I forced myself to do it in C95 without recursion…because it had been a while.

  354. A file of “standard” test cases

    I’ve written a file of 4096 tests of the following form:

    Problem 1
    3
    in [
    1
    2
    4
    ]? no
    
    

    You can find zipped and gzipped versions of the file, a more detailed explanation, and Python code to test your Python function against the file, here.

    I’m amazed how many people thought Mike’s point or Bentley’s point was about not testing code. Of course you should test! The point is whether you can get it right without using testing as a crutch to get there before you test.

  355. function s($n,$h){$f=0;$l=count($h)-1;do{$m=floor(($l+$f)/2);if($h[$m]==$n)return $m;if($f==$l)return;if($n&lt;$h[$m]){$l=$m-1;}else{$f=$m+1;}}while($f&lt;=$l);}

    Developed and tested on the command line. I didn't get it right on the first run.

  356. Emanuel Evans

    Got it wrong the first time (it was a small fix)–here’s the corrected version:

    bsearch :: (Ord a) => a -> [a] -> Maybe a
    bsearch _ [] = Nothing
    bsearch a xs | a == x = Just x
                 | a < x  = bsearch a (fst halves)
                 | a > x  = bsearch a (drop 1 $ snd halves) --didn't do "drop 1" on the first go around, which resulted in an infinite loop
        where halves = splitAt i xs
              x      = xs !! i
              i      = length xs `div` 2
    
  357. private int SearchBinary(int[] ArrayToSearch, int ToFind, int startpos, int endpos)
            {
                if ((endpos - startpos) == 0)
                {
                    if (ArrayToSearch[endpos] == ToFind)
                    { return endpos; }
                    else
                    { return -1; }
                }
                // Testposition - roughly in the middle of endpos and startpos
                int Testposition = (endpos+startpos)/2;
                if (ArrayToSearch[Testposition] == ToFind)
                { return Testposition; }
                else if (ToFind < ArrayToSearch[Testposition])
                { return SearchBinary(ArrayToSearch, ToFind, startpos, Testposition); }
                else
                { return SearchBinary(ArrayToSearch, ToFind, Testposition, endpos); }
            }
    
  358. PHP bi_search – work for first test and every test after. should work for integers, floats, characters. No particular reason for language choice.

     2)
        {
    		if($arr[$half]  $ele)
    			return bi_search(array_slice($arr,0,$half),$ele);
    	}
    	elseif($n == 2)
    		return ($ele == $arr[0] || $ele == $arr[1]);
    	else return ($n == 1 && $arr[0] == $ele);
    		
    }
    ?>
    
  359. My above code has not been tested – C#.

  360. def binsearch( target, offset, alist):
       if not alist:
         return -1
       midInd = len(alist)/2
       midVal = alist[midInd]
       if (midVal == target):
         return offset + midInd
       elif (midVal &lt; target):
         return binsearch(target, offset+midInd+1, alist[midInd+1:])
       else:
         return binsearch(target, offset, alist[:midInd])
    

    It seems to work, except I stupidly forgot the increment in the second case the first time I tried it.

  361. Argh, failed. 2 huge bugs. Shame upon me.

  362. A try:

    public class BinSearch {
      public static final int binSearch(final int[] array, final int target) {
        final int lowIndex = 0;
        final int highOffset = array.length - 1;
        final int middleOffset = highOffset >>> 1;
        
        for (;;) {
          final int middleIndex = lowIndex + middleOffset;
          final int middleElement = array[middleIndex];
          if (target == middleElement) return middleIndex;
          else if (highOffset == 0) return -1;  // If lowIndex == highIndex and not found yet, not going to be found
          else if (target < middleElement)
            highOffset = middleOffset - 1;
          else {
            final int delta = middleOffset + 1;
            lowIndex += delta;
            highOffset -= delta;
          }
          middleOffset = highOffset >>> 1;
        }
      }
    }
    
  363. Python not tested or compiled:

    
    def find(a, l):
        """ return the index of a in the sorted list l or None if it is not there """
        start = 0
        end = len(l) - 1
        while start <= end:
            mid = (start + end) / 2
            if l[mid] == a:
                return mid
            elif a < l[mid]:
                end = mid - 1
            else:
                start = mid + 1
        return None
    
    
  364. I failed.

    I would agree with several other posters that the implement without testing is a strange way to go about development. I could have found my problem if I sat and looked long enough, but instead I ran it, found the problem and fixed it in minutes. *shrug*

  365. Found 1 bug as I tested the code (infinite loop). I needed to change teh condition to be <= 1 and not == 0.

    Time : roughly 2 hours.
    Status: Failed – initial attempt
    Working as of second attempt – not thoroughly tested.

            /// <summary>
            /// Binary Search
            /// </summary>
            /// <param name="ArrayToSearch">Array of ints</param>
            /// <param name="ToFind">Integer to find</param>
            /// <param name="startpos">Starting Position</param>
            /// <param name="endpos">Ending position</param>
            /// <returns>the position (zero base) if found otherwise -1</returns>
            private int SearchBinary(int[] ArrayToSearch, int ToFind, int startpos, int endpos)
            {
                if ((endpos - startpos) <= 1)
                {
                    if (ArrayToSearch[endpos] == ToFind)
                    { return endpos; }
                    else if (ArrayToSearch[startpos] == ToFind)
                    { return startpos; }
                    else
                    { return -1; }
                }
                // Testposition - roughly in the middle of endpos and startpos
                int Testposition = startpos + (endpos - startpos) / 2;
                if (ArrayToSearch[Testposition] == ToFind)
                { return Testposition; }
                else if (ToFind < ArrayToSearch[Testposition])
                { return SearchBinary(ArrayToSearch, ToFind, startpos, Testposition); }
                else
                { return SearchBinary(ArrayToSearch, ToFind, Testposition, endpos); }
            }
    
  366. Andrew Crawford

    So I wrote my binary search in Perl and it worked at first go, just like it will for so many other readers of your blog. I doubt anyone’s day will be enhanced by me posting the code, so please take my word for it.

    I think the reason why only circa 10% of programmers can write a functioning binary search routine is that only circa 10% of programmers have brains wired for implementing low-level algorithms.

    Twenty years ago, “not being able to write low-level algorithms” was the same as saying “not able to program computers” but the past few decades of progress in software development have been aimed specifically at allowing the other 90% – the ones who can’t actually program – to produce useful software despite their handicap. This has been great for the 90%, and for the companies who employ them, but it’s come at a cost to programming culture.

  367. Code at http://gist.github.com/371978

    I actually wrote two implementations. The first was a completely naive recursion version, which worked perfectly.

    The second was an iterative version of the first, but I made a stupid, horrible mistake. When I’d finished, I decided that my extra slice variable was redundant and I could just mutate the original array.

    This wouldn’t have been a problem except that I’d chosen the sentinel value for “not in array” to be the length of the array as opposed to -1. Since I was now mutating the original array, my iterative version would always return index 0 for any element not in the array. WHOOPSIE.

    That’ll teach me to try and be clever.

    On the upside, I was pleasantly surprised when all the assertion failures for the recursive method turned out to be bugs in the tests.

    Also on the up side is that neither function should be susceptible to the overflow bug by virtue of using D’s slicing syntax.

  368. On another note, I think discounting the overflow bug is a bad idea: it’s a bug, end of story. It’s not hard to avoid, given the correct data structure (in this case, slices eliminate it entirely).

  369. I did a unit test. Sorry.

    int binarySearch(vector<int> array, int num) {
      int max = array.size();
      int min = 0;
      int middle = floor(max/2.0);
      int prevMid;
    
      do {
        prevMid=middle;
        int val = array[middle];
        if      ( val > num ) {
          max = middle;
        }
        else if ( val < num ) {
          min = middle;
        } else { return middle;}
        middle = min + (floor(max-min)/2.0);
      }  while (prevMid != middle) ;
      return -1;
    
    }
    
  370. I failed. I wrote the code (below) and ran through 5 simple test cases but it wouldn’t find the element if it was the last in the list.

    I thought this was a great exercise. I haven’t used System.arrayCopy in forever, and 50% of my job is J2EE development!

    	public static boolean bsearch(int[] a, int s) {
    		if(a.length == 1) {
    			if (a[0] == s) {
    				return true;
    			} else {
    				return false;
    			}
    		} else if(a.length == 2) {
    			if (a[0] == s) {
    				return true;
    			} else {
    				return bsearch(new int[] {a[1]}, s);
    			}
    		} else {
    			int midpoint = a.length / 2;
    			if(a[midpoint] == s) {
    				return true;
    			} else if(a[midpoint] < s) {
    				int[] upperHalf = new int[(a.length-1)-midpoint];
    				System.arraycopy(a, midpoint, upperHalf, 0, (a.length-1)-midpoint);
    				return bsearch(upperHalf, s);
    			} else {
    				int[] lowerHalf = new int[midpoint];
    				System.arraycopy(a, 0, lowerHalf, 0, midpoint);
    				return bsearch(lowerHalf, s);
    			}
    		}
    	}
    
  371. Ahh, shouldn’t have made those three variables final and fixed my failure returns.

    Now it should be guaranteed not to overflow and to work properly.

    public class BinSearch {
      public static final int binSearch(final int[] array, final int target) {
        int lowIndex = 0;
        int highOffset = array.length - 1;
        int middleOffset = highOffset >>> 1;
        
        for (;;) {
          final int middleIndex = lowIndex + middleOffset;
          final int middleElement = array[middleIndex];
          if (target == middleElement) return middleIndex;
          else if (target < middleElement) {
            if (middleOffset == 0) return -1;
            highOffset = middleOffset - 1;
          }
          else {
            if (middleOffset == highOffset) return -1;
            final int delta = middleOffset + 1;
            lowIndex += delta;
            highOffset -= delta;
          }
          middleOffset = highOffset >>> 1;
        }
      }
    }
    
  372. This sounds like a lot of fun, but it’s sort of a party novelty. Are you allowed to use backspace to delete, or is it a one shot: write it and release it? I don’t know if I could avoid hitting backspace. It’s rather horribly ingrained by now.

    I don’t really see the point of writing code without testing. The whole point of programming is to write broken code and fix it. Usually I just create an empty source file and start debugging. That’s one of the reasons I like Realbasic. You create an empty program and run it, and up comes a trivial window and a menu with the quit command. It’s a very satisfying starting point, full of possibilities. Then I pop open a text editor and start the spec.

    After all, the last few times I’ve written a binary search were to find an insertion point, not a particular element, and to search a very large, but relatively uniform database by estimating a “mid” point based on the extreme and search values. A old working binary search I had lying around made an excellent starting point. Just replace the axe blade, slip in a new handle, try a different blade, and voila, it’s all debugged.

  373. def search(array, value):
    	oldIndex = 0
    	leftBound = 0
    	rightBound = len(array)
    	while True:
    		index = leftBound + (rightBound - leftBound) / 2
    		if(array[index] == value):
    			return index
    		elif(array[index] > value):
    			rightBound = index
    		else:
    			leftBound = index
    		if(index == oldIndex):
    			return
    		oldIndex = index
    
  374. doh! it should’ve been:

    else if(a[midpoint] < s) {
    				int[] upperHalf = new int[(a.length-1)-midpoint];
    				System.arraycopy(a, midpoint+1, upperHalf, 0, (a.length-1)-midpoint);
    				return bsearch(upperHalf, s);
    			}
    
    
  375. I’m going for extra credit here, posting my Python code before I test it. This isn’t the first time I’ve written a binary search, but it’s been a long time – at least 15 years as best as I can remember. I long ago learned the secret – make sure each pass is narrowing the search range by at least 1, otherwise you can get into an infinite loop.

    To make this more interesting, I made it work with only a less-than operator, similar to the way the standard C++ library works.

    I’m quite surprised that you didn’t include some test conditions, but I see a few comments above mine that at least one person has volunteered. Thanks!

    def binary_search(sorted_seq, target):
        bottom = 0
        top = len(sorted_seq) - 1
        while top >= bottom:
            mid = (top + bottom) / 2
            if target < sorted_seq[mid]:
                top = mid - 1
            elif sorted_seq[mid] < target:
                bottom = mid + 1
            else:
                return mid
        return None
    
  376. Lutz Mueller
    
    def bsearch(arr,val,b=0,e=arr.length-1)
      # trivial cases
      return false if arr.empty?
      if(e-b<2)
        # one or two elements
        return b if(arr[b]==val)
        return e if(arr[e]==val)
        return false
      end
      # recursive case
      pivot = ((b+e)/2).floor
      return pivot if arr[pivot]==val
      return bsearch(arr,val,b,pivot-1) if arr[pivot]>val
      return bsearch(arr,val,pivot+1,e)
    end
    
  377. int mybsearch(const int arr[],const int length,const int target){
        int start=0,end=length-1;
        int index = (start+end)/2;
        int found = 0;
    
        while(start<end){
            if(arr[index]==target){
                found = 1;
                break;
            }
            if(arr[index]<target)
                start=index;
            else if(arr[index]>target)
                end=index;
            index=(start+end)/2;
            if(index==start){
                if(arr[index]==target)
                    found=1;
                else if(arr[++index]==target)
                    found=1;
                break;
            }
        } 
        if(found)
            return index;
        return -1;
    }
    

    Alrighty then, what are the test cases?

  378. Lutz Mueller

    grr, how do I edit this again?

    above source is what I ended up with (Ruby). I failed insofar as one of my test cases produced an endless recursion. I then decided to handle the trivial cases explicitly, with good results.

  379. gist.github.com/371995

  380. Failed on initial try in C with conditional silliness, took a couple more “oh, yeah, that” thoughts to finish.

  381. Hippopotenuse

    Am one of the 90% – didn’t consider zero length input array. I’m ashamed to say I wrote it in IDL.

  382. I didn’t have the stones to post it without trying it out but I did write it all the way before I did. If I’d have been in the class, I’d have been part of the 10%. Woo Hoo!

    <?
    
    for ($i=0; $i<100; $i++){
    	$array[$i]=$i*10;
    }
    
    $desiredNumber=3000;
    
    $found=false;
    $maxIndex=count($array)-1;
    $minIndex=0;
    $trialIndex=floor($maxIndex/2);
    
    do {
    
    	if ($array[$maxIndex]==$desiredNumber){
    		echo "it's at $maxIndex<BR>";
    		break;
    	}
    	elseif ($array[$minIndex]==$desiredNumber){
    		echo "it's at $minIndex<BR>";
    		break;
    	}
    	elseif ($array[$minIndex]<$desiredNumber and $desiredNumber<=$array[$trialIndex]){
    		echo "it might be between $minIndex and $trialIndex<BR>";
    		$maxIndex=$trialIndex;
    	}
    	elseif ($array[$trialIndex]<=$desiredNumber and $desiredNumber<$array[$maxIndex]){
    		echo "it might be between $trialIndex and $maxIndex<BR>";
    		$minIndex=$trialIndex;
    	}
    	else{
    		echo "it's outside of the range of values in the array<BR>";
    		break;
    	}
    	
    	if ($maxIndex-$minIndex<=1){
    		echo "there is no element with that value<BR>";
    		break;
    	}
    	
    	$trialIndex=floor(($maxIndex-$minIndex)/2)+$minIndex;
    
    }
    while ($found!=true);
    
  383. Ok crap, my code fails if the array has a length of 1. Need to use

    start<=end

    instead of

    start<end

    as the while condition. FAIL.

  384. Ugly and inelegant, but it works. I hope.
    Seems to, anyways.

    Running:

    % while (true); do rv=`./bs | grep "Found" | sed -e 's/.*in //'`; echo "1" >> $rv.bs; done
    ^C
    
    % wc -l *.bs
      789 10.bs
     1650 11.bs
     3351 12.bs
     5122 13.bs
     2647 14.bs
      264 7.bs
      399 8.bs
      394 9.bs
    14616 total
    
    % more bs.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    int binsear (int *, int, int, int *);
    
    int compare_int (const void *a, const void *b) {
    
     const int *da = (const int *)a;
     const int *db = (const int *)b;
    
     return (*da > *db) - (*da < *db);
    }
    
    
    int main (int argc, char *argv[]) {
    
    
            int values[100];
    
            int t,sv,sr,i,itr;
    
            for(i=0;i<100;i++) {
                    values[i]=rand()%10000;
            }
    
            qsort(values, 100, sizeof(int), compare_int);
    
            sr=time(NULL);
            srand(sr);
            t=rand()%100;
            sv=values[t];
    
            printf("rand: %d \n T value: %d [%d]\n",sr,sv, t);
    
            printf("Found %d @ [%d]",sv, binsear(values,100,sv,&itr));
            printf(" in %d\n",itr);
            return 0;
    }
    
    int binsear(int *values,int size, int sv, int *itr) {
    
            int *sptr,*bptr,*tptr;
            int cval,*cptr;
            int cind=0;
    
            sptr=bptr=values;
            tptr=values+(size);
            cval=-1;
    
            *itr=0;
            while (sv != cval) {
    
                    (*itr)++;
                    cind+=(tptr-bptr)/2;
                    cptr=sptr+cind;
                    cval=*cptr;
    
                    if(cval == sv) {
                            return(cind);
                    }
    
                    if(sv > cval) {
                            bptr=cptr++;
                    } else {
                            cind-=(tptr-bptr)/2;
                            tptr=cptr--;
                    }
                    if (bptr==tptr || (cptr > sptr+size || cptr < sptr)) {
                            printf("Error: %p %p %p %d %d\n",bptr,tptr,cptr,*cptr,sv);
                            exit(2);
                    }
            }
    
            return cind;
    }
    
  385. Success! I wrote a recursive version first:

    struct array_elem
    {
    int key;
    char *value;
    };

    char *bin_search(struct array_elem *array, int count, int key)
    {
    if (count==0)
    {
    return NULL;
    }
    else
    {
    int split_ix = count/2;
    int split_value = array[split_ix].key;
    if (split_value \> key)
    {
    return bin_search(array, split_ix, key);
    }
    else if (split_value \ key)
    {
    count = split_ix;
    }
    else if (split_value \< key)
    {
    array += split_ix + 1;
    count -= split_ix + 1;
    }
    else
    {
    return array[split_ix].value;
    }
    }
    }
    }

    Both work on the handful of test cases I've tried, so there might be some bugs. I'm curious what the common mistakes are.

  386. I take it back. I misses some values with the floor() calculation. Bummer.

  387. I’m going to own up to a failure on the first attempt.

    I started writing it after reading the Programming Pearls quote rather than later in the article, so I didn’t see the “no testing” admonition until later.

    For posterity:

    int binsearch(int a[], int nelem, int val)
    {
        int i = 0, j = nelem - 1;
    
        while (i < j)
        {
            int k = (i + j) / 2;
    
            if (a[k] < val)
                i = k + 1;
            else
                j = k;
        }
    
        if (a[i] == val)
            return i;
        else
            return -1;
    }
    
  388. I got syntax errors on my first try, but after I cleaned them up the code passed my tests:

    <?php
    
    function array_bsearch($val, array $sorted_array)
    {
    	$length = count($sorted_array);
    	if ($length == 0) return FALSE;
    
    	$mid = $length/2;
    	$mid_val = $sorted_array[$mid];
    
    	if ($val == $mid_val) return $mid;
    
    	$half_array = ($val < $mid_val)? 
    			array_slice($sorted_array, 0, $mid):
    			array_slice($sorted_array, $mid + 1);
    
    	return array_bsearch($val, $half_array);
    }
    
    ?>
  389. My code worked the first time, but my test had a bug. (When the haystack had duplicates, it would want bsearch to magically give back the correct one.)

    def bsearch(arr, elem):
    	begin = 0
    	end = len(arr)
    	
    	while begin < end:
    		if begin == end - 1:
    			if arr[begin] == elem: return begin
    			else: return None
    		mid = (begin + end) / 2
    		
    		if elem < arr[mid]: end = mid
    		else: begin = mid
    
    import random
    
    random.seed(hash("good news everybody"))
    
    for i in xrange(1000):
    	n = random.randrange(10, 100)
    	numbers = [random.randrange(0, 100) for _ in xrange(n)]
    	numbers.sort()
    
    	for index in xrange(len(numbers)):
    		result = bsearch(numbers, numbers[index])
    		if numbers[index] != numbers[result]:
    			print 'Oh noes! %d vs %d (looking for %d in %r)'  %\
    				(index, result, numbers[index], numbers)
    		
    		not_here = numbers[index] + 1
    		if not_here not in numbers:
    			result = bsearch(numbers, not_here)
    			if result:
    				print "I taught I saw a putty tat (%d) at %d, but I didn't (%r)" %\
    					(not_here, result, numbers)
    
  390. def bsearch(the_list, the_val):
        if len(the_list) == 0:
            return False
    
        left = 0
        right = len(the_list)-1
        middle = (left+right)/2
    
        while left != right:
            if right-left == 1:
                return (the_list[left] == the_val) or (the_list[right] == the_val)
            elif the_val == the_list[middle]:
                return True
            elif the_val < the_list[middle]:
                right = middle
            else:
                left = middle
            middle = (left+right)/2
    
        return the_list[left] == the_val
    
  391. I had no trouble implementing a correct solution, although sample arrays to test with probably would be nice

  392. doh, meant C89 not C95

  393. Here’s my attempt, with associated tests:

    def bsearch(thing, the_list):
        """A binary search to find thing in the_list.
           Returns the index, or None if it's not found."""
    
        assert the_list == sorted(the_list)
        if the_list == []:
            return None
            
        middle = len(the_list) // 2
        print thing, "vs.", middle, the_list
        
        if the_list[middle] == thing:
            return middle
        elif the_list[middle] > thing:
            return bsearch(thing, the_list[:middle])
        else:
            result = bsearch(thing, the_list[middle+1:])
            if result is not None:
                return middle + 1 + result
            else:
                return None
    
    print bsearch(0, range(10))
    print bsearch(4, range(10))
    print bsearch(9, range(10))
    print bsearch(10, range(10))
    

    I had two bugs (so far). The first was that I got the middle comparison around the wrong way, and the second was that I forgot to add 1 to the final result. I might have a go at an iterative version later on…

  394. I failed. Though I ended up with a correct implementation, I went and coded it before I saw the “no testing” rule, and my code was not correct for my first test.

  395. Here’s my Haskell solution, at least a bit different than others posted so far:

    import Data.Array
    
    binarySearch :: (Ord e, Ix i, Integral i) => Array i e -> e -> Maybe i
    binarySearch arr x = let (a,z) = bounds arr in go a z
        where go lo hi | lo > hi         = Nothing
                       | (arr!mid) == x  = Just mid
                       | (arr!mid) < x   = go (mid + 1) hi
                       | otherwise       = go lo (mid - 1)
                where mid = (lo + hi) `div` 2
    
  396. So, here’s my 1 hour JS tryout. It’s not tested so hopefully I nailed it!

    function bsearch (ar, T) {
      var l = 0   
        , m
        , u = ar.length;
      
      do{
        m = Math.floor( l+(u-l)/2 );
        
        if( T === ar[m] ){ return true; }
        
        T > ar[m] ? l = m+1 // Lower is inclusive
                  : u = m;  // upper is not
      }while(u > l);
      
      return false;
    }
    
  397. David McCammond-Watts

    I used Delphi

    // Returns the index of Item in Arr or -1 if not found
    function BinarySearch(const Arr: array of Integer; Item: Integer): Integer;
    var
      Left, Right: Integer;
    begin
      Left := 0;
      Right := High(Arr);
      while Left <= Right do begin
        Result := (Left + Right) div 2;
        if Item < Arr[Result] then
          Right := Result - 1
        else if Item > Arr[Result] then
          Left := Result + 1
        else
          Exit;
      end;
      Result := -1;
    end;
    
  398. Seems my last comment was truncated, here a new try:

    #!/usr/bin/env ruby
    
    def rec_binsearch( tab, search, min, max )
        if min > max
            return -1
        end
        med=(max + min)/2
        if tab[med] == search
            return med
        end
        if tab[med] =0
        puts %{tab[#{res}]=#{tab[res]}}
    else
        puts %{can't find #{search} in [#{tab.join(' ')}]}
    end
    

  399. programmer barbie

    I failed. Programming is hard, let’s go shopping!

  400. Finally I protected my ‘<‘ by HTML code…

    #!/usr/bin/env ruby
    
    def rec_binsearch( tab, search, min, max )
        if min > max
            return -1
        end
        med=(max + min)/2
        if tab[med] == search
            return med
        end
        if tab[med] < search
            min=med+1
        else
            max=med-1
        end
        return rec_binsearch( tab, search, min, max )
    end
    
    def binsearch( tab, search )
        min=0
        max=tab.length
        return rec_binsearch(tab,search,0,tab.length-1)
    end
    
    search=ARGV[0].to_i
    tab=ARGV[1..-1].map{ |x| x.to_i }
    res=binsearch(tab,search)
    if res>=0
        puts %{tab[#{res}]=#{tab[res]}}
    else
        puts %{can't find #{search} in [#{tab.join(' ')}]}
    end
    
  401. In ruby, in the spirit of Bently’s code description:

    a = [1,2,3,5,6,7,8,9,11]
    sought = 4
    found = false
    range = a.size
    base = 0
    while range > 0 && !found
      mid = range / 2 + base
      lower_range = upper_range = range / 2
      upper_range -= 1 if range.even?
      if a[mid] == sought
        found = true
      elsif a[mid] < sought
        base = mid + 1
        range = upper_range
      else
        range = lower_range
      end
    end
    puts "found: #{found}"
    

    I had to mentally test this before getting the upper and lower range calculation correct.

    @Kaleberg: “An old working binary search I had lying around made an excellent starting point.” – agree, usually I find this to be a superb strategy!

  402. An iterative version. I had a bit more trouble with this one, it went into a spin and I had to limit the number of iterations:

    MAX_ITER = 10
    
    def bsearch(thing, the_list):
        """A binary search to find thing in the_list.
           Returns the index, or None if it's not found."""
        iters = 0    
        assert the_list == sorted(the_list)
        
        low = 0
        high = len(the_list)
        
        print thing, the_list
        while 1:
            iters += 1
            if iters > MAX_ITER:
                return "Aargh!"
                
            mid = (low+high) // 2
            mid_value = the_list[mid]
            print low, mid, high
            
            if low == high:
                return None
            if mid_value == thing:
                return thing
            elif mid_value > thing:
                high = mid
            else:
                low = mid
    
    print bsearch(0, range(10))
    print bsearch(4, range(10))
    print bsearch(9, range(10))
    print bsearch(10, range(10))
    

    I had a number of bugs in this one, which took a while to work out. The first was that I (again!) had my comparison around the wrong way, and the second is that I’m not sure that there’s a clean way to prevent my code going into an infinite loop for non-existent values. I have an off-by-one error in there, perhaps?

    Yep, so I’m a terrible programmer – I’ll go hang my head in shame now… but read up on binary sort :)

  403. Jeremy Holman

    Many commenters have said that this challenge is meaningless, since there is no real-world application of coding without testing. I disagree.

    First, I have found that practicing coding on paper forced me to learn better habits about correctness, which results in higher coding speed. So [if you’re like me], doing drills of this non-interactive nature will improve your productivity.

    Second, frankly, if you can’t manage this elementary a case (without tests), how can you trust yourself to come up with all the relevant tests? Consider those who posted their allegedly-tested programs and were incorrect. There but for the grace of formal proof methods go I.

    Oh, and I wrote it in 10 or 15 minutes, recursively in Python, and believe it to be error-free. Let’s see if I can post it successfully:

    def binser2(t,a,begin,end):
        if begin == end: return False
        mid = (begin+end) / 2
        if a[mid] == t: return mid
        if t < a[mid]: return binser2(t,a,begin,mid)
        return binser2(t,a,mid+1,end)
    
    def binser(t,a):  # t is the target, a is the array
        return binser2(t,a,0,len(a))
    
  404. Success. I just want to point out that this is a really terrible problem. Given the rules, it is not surprising that 90% fail, and it is not really a measure of programmer skill or productivity.

    int bs(int* array, int count, int value) {
    	int min = 0;
    	int max = count;
    	while (min < max) {
    		int mid = (max + min) / 2;
    		if (array[mid] == value)
    			return mid;
    		else if (array[mid] > value)
    			max = mid;
    		else
    			min = mid + 1;
    	}
    	return -1;
    }

    I could have written this, debugged it, and written a battery of unit tests in five minutes. Instead it took me about 25 minutes of being a human compiler/computer before I was confident enough to compile and run it. I used pen and paper to test it by hand and clean out the bugs. This is what I have a problem with; I could have just used the computer to do this for me. I found this problem incredibly frustrating because I was stripped of everything that I consider makes me a good programmer.

    Here’s the test I ran against it in case anyone was wondering. Not terribly thorough, but I really don’t care.

    int test[] = {1, 3, 4, 7, 9, 10, 11, 17, 18, 19, 25, 26, 29, 30};
    
    int main(void) {
    	int count = sizeof(test) / sizeof(int);
    	for (int i = 0; i < 35; ++i)
    		printf("searching %i: position %i\n", i, bs(test, count, i));
    	return 0;
    }

    All values in the output matched up with their position in the array. I also had no compiler errors or warnings in the implementation of bs() on first compile (as C99); I got an error first because gcc compiles in C89 by default (so it complained about the for loop declaration), and I got a warning because I forgot to include stdio.h (technically part of the test, since the algorithm does not need that header.)

  405. Erik Swanson

    I believe this is correct. No promises though, I’m a novice coder.

    #!/usr/bin/python                                                                                   
    
    def half(length):
        return int(length*.5)
    
    def search(array,T):
        range = [half(len(array)), 0, len(array)]
        value = array[range[0]-1]
        while value != T:
            if value > T:
                range[2] = range[0]
                place = 0
            elif value < T:
                range[1] = range[0]
                place = 1
            range[0] = half(range[0]) + place*range[0]
            value = array[range[0]-1]
        return range[0]
    
  406. Language this month=Actionscript 3.

    public function binarySearch(array, val):int
    {
    	return binarySearchHelper(array, val, 0, array.length);
    }
    
    public function binarySearchHelper(array, soughtValue, startIndex, endIndex):int
    {
    	trace((startIndex +" "+ endIndex));
    	if(startIndex >= endIndex)
    		return -1;
    	
    	var halfway:int = Math.floor((endIndex+startIndex)/2);
    	
    	if(array[halfway] == soughtValue)
    		return halfway;
    	
    	if(array[halfway] > soughtValue)
    	{
    		return binarySearchHelper(array, soughtValue, startIndex, halfway); 
    	}
    	return binarySearchHelper(array, soughtValue, halfway, endIndex);
    }
    

    Did not work the first try, as I had mixed up my . (This is the fixed version.)

  407. Woops, that was supposed to be: “Did not work the first try, as I had mixed up my less-than and greater-than signs”

  408. Oh also, I should mention I tested mine on paper for (almost) all branches for array sizes zero through five (yes, this is why I am frustrated.) So I am confident that it is correct.

    I still don’t think these rules are fair. For instance my code obviously fails integer overflow, as others have mentioned in their own code. Does this even matter? I wouldn’t expect an algorithm of bs() to guard against this in any way besides an assert(). I don’t consider this an error.

    I find it silly that putting – instead of +, for example (as I did in (min + max) / 2, luckily I noticed it before compiling) is considered failure, which is something that would immediately be caught if we were allowed to test; and yet, people here are claiming success with recursive solutions in languages without tail-call optimization, so they have O(log n) space requirements (the algorithm should be O(1) memory.)

    This can’t possibly be graded pass/fail. A question like this on a paper test makes sense if you can still get part marks for a decent attempt.

    Anyway. /complaining

  409. Michael Shaw

    Failed on first attempt, forgot to exclude the middle element in the recursive call:

    def bsearch(in_list, target):
    if len(in_list) == 0:
    return False
    else:
    middle_index = len(in_list) / 2
    middle = in_list[middle_index]
    if middle == target:
    return True
    elif middle LESSTHAN target:
    return bsearch(in_list[middle_index + 1:], target)
    else:
    return bsearch(in_list[:middle_index], target)

  410. Not tested yet. Thirty minutes. Revolution, written recursively, which glancing at some of the other solutions I’m not sure I prefer now. I wrote it to take a list rather than an array.

    function bSearch k,pList,pOffset
       -- k, value to find
       -- pList, the (sorted) list to search
       -- pOffset, where the current list starts relative to the start of the original list
       -- returns the index of of k in pList
       -- call with pOffset empty
       if pList is empty then return false -- not found
       if pOffset is empty then put 0 into pOffset -- FYI, Revolution uses 1-based lists
       put (1 + the number of items of pList) div 2 into tMidPoint -- 4 = 2, 5 = 3, 6 = 3, 7 = 4, etc.
       put item tMidPoint of pList into tMidValue
       switch
          case tMidValue < k -- k is in the top half if at all
             return bSearch(k,(item tMidPoint + 1 to -1 of pList),(pOffset + tMidPoint))
          case tMidValue > k -- k is in the bottom half if at all
             return bSearch(k,(item 1 to tMidPoint - 1 of pList),pOffset)
          case tMidValue = k -- found it!
             return pOffset + tMidPoint
          default
             return false -- not found
       end switch
    end bSearch
    
  411. Not quite clean.

    #!/usr/bin/env ruby
    
    class BinarySearch
      attr_reader :items, :target
    
      def initialize(sorted_items, target)
        @items = sorted_items || []
        @target = target
      end
    
      def run
        if target and items.size > 0
          search(0, items.size - 1)
        else
          false
        end
      end
    
    private
      def search(index_first, index_last)
        # termination conditions
    
        # only one left
        if index_first == index_last
          # match?
          if target == items[index_first]
            return true
          else
            return false
          end
        end
    
        # never found. index of the first is the larger than the last
        return false if index_first > index_last
    
        # never found. target is smaller than first item
        return false if items[index_first] > target
    
        # never found. target is larger than last item
        return false if items[index_last] < target
    
        # recusive
        # division, index_first..index_mid, index_mid + 1..index_last
        index_mid = (index_last + index_first) / 2  # it is floor in integer division
    
        # in first half?
        if search(index_first, index_mid)
          return true
        # in second half?
        elsif search(index_mid + 1, index_last)
          return true
        # both not found
        else
          return false
        end
      end
    end
    
    if ARGV.size >= 0
      items = (ARGV[1..-1] || []).sort
      target = ARGV[0]
    
      puts "Items : [#{items.join(",")}]"
      puts "Target: #{target}"
      puts "--"
      puts "Result: #{if BinarySearch.new(items, target).run then "Found" else "Not found" end}"
    else
      puts "Usage: bs <target> <list of items>"
    end
    
    
  412. David Foster

    Got it right the first time.

    public class Search {
    	public static void main(String[] args) {
    		int[] a = new int[] {2,4,6};
    		System.out.println(binarySearch(a, 7));
    	}
    	
    	static int binarySearch(int[] a, int n) {
    		int min = 0;
    		int max = a.length - 1;
    		
    		while (min <= max) {
    			int mid = (min + max)/2;
    			
    			int value = a[mid];
    			if (value == n) {
    				return mid;
    			} else if (value < n) {
    				min = mid + 1;
    			} else if (value > n) {
    				max = mid - 1;
    			}
    		}
    		return -(min+1);	// insertion point for non-found element
    	}
    }
    

    There are a few reasons why this algorithm is tricky.

    * If you use a lim-index instead of a max-index, you’re likely to access outside the buffer when searching for an element higher than the array max. Fencepost error.

    * It’s easy to forget the case of searching for a non-existent element. In the case above, I’ve even provided a return value that tells you (unambiguously) where the insertion point would be for a non-existent element.

  413. Five minutes, and Python, obviously:

    def binary_search(collection, value):
    “””returns the index”””
    if not collection:
    return None
    if collection[0] == value:
    return 0
    if collection[0] > value:
    return None
    if collection[-1] == value:
    return len(collection) -1
    if collection[-1] < value:
    return None

    # Bisect:
    mid = len(collection) // 2
    if mid == 0:
    # Done:
    return 0
    if collection[mid] == value:
    return mid
    if collection[mid] value:
    return binary_search(collection[:mid], value)

    Seems to work, although I have only made like three tests. :) I find the idea of a challenge where you aren’t allowed to actually run your code moronic. This isn’t testing for how good programmers are, it’s more about luck, IMO. I’d say the passage in the book is outdated, because now we have something called test-driven development. :)

  414. Followup: seems to work. Tested:
    1. target is in list
    2. target is at start of list
    3. target is at end of list
    4. target is not in list
    5. target is less than smallest value in list
    6. target is greater than largest value in list
    7. target is empty
    8. list is empty
    9. target and list are empty

  415. sure not the fastest but kinda readable for me:

    def find(array, what) 
      return what == array[0] if array.size == 1
    
      mid = array.size/2
    
      lower = array[0..mid-1]
      higher = array[mid..array.size]
    
      result = find(lower, what)
      result = find(higher, what) unless result
    
      result
    end
    
    a = [1,2,4,6,7,10]
    
    (0..10).each do |x|
      puts x if find(a, x)
    end
    
  416. @dewb:
    > Testing is part of software development. This challenge is a bit like asking artists to draw a perfect circle while blindfolded. It’s a neat parlor trick, but you can’t use it as a litmus test for a “real artist” or “real programmer.”

    My daughter went to art school, and one of the exercises they used was to draw something without ever looking at the paper. So perhaps your analogy is more confirming than refuting.

  417. //C program written in comment window, no compile check.
    // Tab character is saved to clipboard.
    int search(int *list, int high, int low, int key)
    {
    	int mid;
    	mid = (low + high) / 2;
    	if (high == low)
    	{
    		return key;
    	}
    	else if (key  list[mid])
    	{
    		return search(list, mid + 1, high, key);
    	}
    }
    
  418. How are you going to test all these!? I see just about every programming language imaginable here!

  419. Just a small rewrite of my previous version (shortened the variables’ names to 1-2 letters to see how short can it get :D) + unit testing to ensure that it works as designed. Looks like it does:

    def search(x, a, m = a.size / 2)
      return nil if x < a.first or x > a.last
      return x if x.eql?(a[m])
      search x, a[m] > x ? a[0,m] : a[m..-1]
    end
    
    if $PROGRAM_NAME.eql?(__FILE__)
      require 'test/unit'
      class TestSearch < Test::Unit::TestCase
        MaxVal      = 1000
        SearchSpace = Array.new(MaxVal / 2){rand MaxVal}.uniq.sort.freeze
    
        def test_must_find_all_values_that_exist_in_search_space
          SearchSpace.each {|x| assert_equal x, search(x, SearchSpace)}
        end
    
        def test_must_return_nil_for_values_not_found_in_search_space
          ((0...MaxVal).to_a - SearchSpace).each {|x| assert_nil search(x, SearchSpace)}
        end
      end
    end
    
  420. Following the prototype of C99 bsearch():
    , I wrote this:

    http://pastebin.com/i8UGMkJi

    Lots of room for improvements, but meh. I’m going to steal other people’s tests now and check it out.

  421. This was actually the first time I ever implemented the binary search. And I actually think I got it right, too!

    Source code in PHP follows. Wrote some basic tests too, but don’t want to waste space.

    # non-recursive binary search
    function bs( $needle, $a )
    {
    	$start = 0;
    	$end = count( $a ) - 1;
    
    	while( $start < $end ) {
    		# middle is relative to start
    		$mid = $start + ceil( ($end - $start) / 2 );
    		if( $needle > $a[$mid] ) {
    			$start = $mid + 1;
    		} else if( $needle < $a[$mid] ) {
    			$end = $mid - 1;
    		} else {
    			# lucky shot!
    			$start = $end = $mid;
    		}
    	}
    
    	# $start = $end now, which might be a match .. or not.
    	if( $needle == $a[$start] ) {
    		return $start;
    	} else {
    		return false;
    	}
    }
    
  422. Untested Javascript. (A bit bold since I’m new to the language.)

    // Return an i such that array[i] === key if possible, else null.
    // Pre: array is sorted ascending.
    function search(array, key) {
        for (var lo = 0, hi = array.length; lo < hi; ) {
            // Inv: 0 <= lo < hi <= array.length
            // Inv: key is in [lo..hi) if anywhere
            // Termination counter: hi - lo
            var mid = lo + ((hi - lo) >> 1);
            if (array[mid] < key)
                lo = mid + 1;  // hi'-lo' = hi-mid-1 = hi-lo-((hi-lo)>>1)-1
            else if (array[mid] === key)
                return mid;
            else
                hi = mid;      // hi'-lo' = mid-lo = (hi-lo)>>1
        }
        return null;
    }
  423. Apparently Adobe have found similar results. Years ago Sean Parent posted that they used the test on interview candidates and saw high failure rates. The only article I can find now is http://stlab.adobe.com/wiki/images/8/8c/Boostcon_possible_future.pdf (warning – this document contains a binary search implementation).

  424. #!/usr/bin/env python
    
    from math import log
    from random import randint
    
    random_array = sorted(list(set([randint(0, x) for x in range(20000)])))
    
    def binary_search(R, T):
        iteration = 1
        N = len(R)
        middle = R[N / 2]
        print 'Searching %s item array for %s' % (N, T)
        
        while(T != middle):
            if len(R) == 1:
                print '*** not found after %s iterations ***' % iteration
                return
            iteration += 1
            
            if T < middle:
                R = R[:len(R)/ 2] 
            else:
                R = R[len(R) / 2:]
                
            middle = R[len(R) / 2]
        print 'Found %s on iteration #%s' % (T, iteration)
    
        
    
    if __name__ == '__main__':
        binary_search(random_array, randint(0, len(random_array)))
    
  425. Not tested:

    int bs(int *array, int value, int first, int last){
        if(last - first <= 1){ 
            if(array[first] == value){
                return first;
            }   
            else if(array[last] == value){
                return last;
            }   
            else {
                return -1; 
            }   
        }   
        else {
            int new_first, new_last, fulcrum;
    
            fulcrum = (first + last)/2;
    
            if(array[fulcrum] >= value){
                new_first = first;
                new_last = fulcrum, first;
            }   
            else {
                new_first = fulcrum + 1;
                new_last = last;
            }   
    
            return bs(array, value, new_fist, new_last);
        }   
    }
    
  426. My first try failed. I had to switch my first two if-statements around and now it (looks like it) works:

    int bsearch(int* haystack, int left, int right, int needle) {
    int middle = (right + left)/2;

    if (haystack[middle] == needle) {
    return middle;
    }
    if (left >= right) {
    return -1;
    }
    if (haystack[middle] > needle) {
    return bsearch(haystack, left, middle, needle);
    }
    if (haystack[middle] < needle) {
    return bsearch(haystack, middle+1, right, needle);
    }
    }

  427. Alright, it actually looks correct. Tried out of range values as well as valid ones, empty array as well as a huge one, and I confirmed the results with a debugger, just in case.

    Now, here are some bugs that 20 minutes of thinking solved (without testing!):

    * When computing the middle index, avoid additions. They might overflow for large arrays. I’ve learned that in the past, the HARD way.
    * Testing for (!found) usually leads to infinite loops, so I ditched that approach quickly.
    * I started writing a binary sort…then I decided to read the specifications! :-)
    * Although this is a typical example of a recursive algorithm, I’m not comfortable enough with recursion (I know, shame on me…), so I decided to go iterative..
    * Do not mix semantics. If you decide your binary_search() should return an index into the array, this will impact your implementation – using uints instead of plain ints for example, which may or may not lead to nasty bugs if you’re not careful.

  428. Full disclosure:

    You shouldn’t probably include me in the statistics, because I did test my code a good amount of times. But I only tried it because I was doing the binary search in Haskell, but I’ve only been playing with Haskell for a few days, and with limited time, so I am far from truly getting it.

    That being said, after a few times, i got this:

    binSearch2 :: [Int] -> Int -> Bool
    binSearch2 list number 
    	| (null list) = False
    	| ((getMiddleValue list) == number) =
    		True
    	| ((getMiddleValue list) > number) =
    		binSearch2 (fst (splitInHalf list)) number
    	| otherwise =
    		binSearch2 (snd (splitInHalf list)) number
    
    getMiddleValue :: [Int] -> Int
    getMiddleValue list 
    	| ((length list) == 1) = list !! 0
    	| otherwise = (last (fst(splitInHalf list)))
    
    splitInHalf :: [Int] -> ([Int],[Int])
    splitInHalf list = (splitAt (div (length list) 2) list)
    

    Like I said before, I’ve only been playing with Haskell for a few days, but it seems to work. Probably not the best way to do it, and maybe I didn’t even exactly follow the binarySearch algorithm (I think I did…did I?), but thats what I came up with.

    Hope you guys like it :D


  429. int binsrch(int count, int *integers, int n)
    {
    int mid;
    int *ptr = integers;

    while (count > 0)
    {
    mid = count / 2;

    if (ptr[mid] == n) {
    return mid + (ptr - integers);
    }

    if (ptr[mid] < n) {
    ptr = ptr + mid + 1;
    count -= mid + 1;
    }
    else {
    count = mid;
    }
    }

    return -1;
    }

  430. @Kragen Javier Sitaker: nope it won’t, BUT it isn’t correct either:

    var a = new Array(2);
    document.write("result (should be 0): " + bsearch(a, 2, 0, a.length));
    

    That case fails. So I’m not in the 10%, shame on me! :)

  431. Yup, overlooked a silly bug in mine, fail++

  432. Dmitri Arkhipov

    Python, not tested

    def bSearch(sortedList, T):
        if( len(sortedList) == 1 ):
            if(sortedList[0] == T):
                return True
            else:
                return False
        else:
            middleIndex = int( (len(sortedList)/2) )
            return ( bSearch(sortedList[:middleIndex], T) or bSearch(sortedList[middleIndex:], T))
    

    Addendum: as a recent Compsci Grad I am contractually obligated to know nothing and fail consistently. Therefore all errors found here-in are intentional and are to be commended thoroughly .

  433. Doh, fails when the search item is not in the container. To my defense, it’s 4:30 in the morning and I’m going to sleep now.

    Note to self: if you get up at 4 in the morning to get a drink of water, check the time on your laptop and see that someone sent you a link to a blog post, ignore it until morning.

    import random
    
    l = set()
    for _ in xrange(500):
        l.add(random.randint(0, 50000))
    
    l = sorted(l)
    pin = random.choice(l)
    
    print "Looking for", pin, "in", l
    left, right = 0, len(l)-1
    iters = 0
    
    while l[left] != pin:
        iters += 1
        half = (right - left) / 2
        if pin > l[left + half]:
            left += half
        elif pin < l[left + half]:
            right -= half
        else:
            print "Found at position", left + half, "after", iters, "iterations"
            break
    
    print "All done"
    
  434. Robert Leffmann

    Plain C code, worked at first try though I did test it before submitting here, which took some time before I realized that while the binary search was working, my small test code was faulty.

    int binsearch(int value, int *array, int size)
    {
      int start = 0, end = size-1;
    
      while(end >= start)
      {
        int index = (start+end)/2;
    
        if(array[index] == value)
          return index;
    
        if(value < array[index])
          end = index-1;
        else
          start = index+1;
      }
    
      return -1;
    }
    
  435. 5 minutes to write, another minute to fix syntax errors; untested

    /* -1: not found, otherwise array index */
    
    ssize_t bin_search(int array[], size_t size, int key)
    {
    	size_t low = 0, high = size;
    	while(low < high) {
    		size_t middle = (low + high) >> 1;
    		if (key == array[middle]) return middle;
    		if (key < array[middle]) high = middle;
    		else /* key > array[middle] */ low = middle + 1;
    	}
    	
    	return -1;
    }
    

    after reading the description further and encountering the part about “index overflow”, I was banging my head on the table as I fell in exactly that trap :/ for “integers” it does not matter, but for “chars”, it would

  436. int BinarySearch(int *arr, int arrSize, int findThis)
    {
    	if (arr == NULL || arrSize > 1);
    
    		if ((minIndex >= arrSize ||
    			 maxIndex  findThis)
    			maxIndex = poolIndex - 1;
    		else if (arr[poolIndex] < findThis)
    			minIndex = poolIndex + 1;
    	}
    
    	return -1;
    }
    
  437. Robert Leffmann

    I just saw you have more than 400 replies to this entry, and you only posted last night!

    Why do you think your blog has exploded in popularity? The catchy title, the uncooked fish (sorry I had to) or the witty writing?

    I think the results of this experiment will come out differently than the 10% success rate mentioned. Telling people to only post anonymously so they can’t receive shame nor credit for their code would have been better.

    With the amount of readers you have on your blog, you could conduct some pretty interesting experiments.

  438. I have used the same prototype of bsearch. Didn’t test it.

    #include <stdio.h>
    #include <stdint.h>
    #include <errno.h>
    
    typedef int (*compare_func_t) (void *, void *);
    extern int errno;
    
    static compare_func_t  compare_func;
    static int compare_elem_size = 0;
    
    void * bin_search(void * a, void * look_for, int start, int end)
    {
        int mid, ret;
        char * input = a, *output = NULL;
        unsigned long long offset;
    
        if (start > end) {
            return NULL;
        }
        mid = (end - start)/2 + start;
        offset = mid * compare_elem_size;
        ret = compare_func(&input[offset], look_for);
    
        if (ret < 0) {
            return bin_search(a, look_for, start, mid - 1);
        } else if (ret > 0) {
            return bin_search(a, look_for, mid + 1, end);
        }
        output = &(input[offset]);
        return (void *)output;
    }
    
    void * binary_search(void * a, void * look_for, 
                         int (*cmp_func)(void *, void *), int size, int num_elem)
    {
        if (num_elem <= 0 || num_elem > INT32_MAX) {
            errno = -EINVAL;
            return NULL;
        }
        if (cmp_func == NULL || size == 0) {
            errno = -EINVAL;
            return NULL;
        }
        compare_func      = cmp_func;
        compare_elem_size = size;
        return bin_search(a, look_for, 0, num_elem -1);
    }
    
  439. After testing it looks good.

    I tested with random values wich resulted in arrays with repeated values. That is something I didn’t have in mind when wrining the code. It turns out my function returns the first of the repeated values, which is nice. Lucky me :)

    The code (again) and the random test:

    #include <time.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int bs(int *array, int value, int first, int last){
        if(last - first <= 1){
            if(array[first] == value){
                return first;
            }
            else if(array[last] == value){
                return last;
            }
            else {
                return -1;
            }
        }
        else {
            int new_first, new_last, fulcrum;
    
            fulcrum = (first + last)/2;
    
            if(array[fulcrum] >= value){
                new_first = first;
                new_last = fulcrum, first;
            }
            else {
                new_first = fulcrum + 1;
                new_last = last;
            }
            return bs(array, value, new_first, new_last);
        }
    }
    
    int compar(const void *a, const void *b){
        if(*(int*)a < *(int*)b)
            return -1;
        if(*(int*)a > *(int*)b)
            return 1;
        return 0;
    }
    
    #define ARR_SIZE 10
    #define MAX_RND 15
    int main(){
        int array[ARR_SIZE];
        int i, value, ret;
    
        srandom(time(NULL));
        for(i=0;i<ARR_SIZE;i++){
            array[i] = random() % MAX_RND;
        }
        qsort(array, ARR_SIZE, sizeof(int), compar);
    
        printf("       ");
        for(i=0;i<ARR_SIZE;i++){
            printf("%2d ",i);
        }
        printf("\nArray: ");
        for(i=0;i<ARR_SIZE;i++){
            printf("%2d ",array[i]);
        }
        printf("\n");
    
        for(i=0;i<15;i++){
            value = random() % MAX_RND;
            printf("value: %d, position: %d\n", value, bs(array, value, 0, ARR_SIZE-1));
        }
    }
    
  440. Written in Redcode. :-) Worked first time but I did a couple of test cases on paper while I was coding. Is that cheating?

  441. #include <iterator> // std::iterator_traits
    
    // binsearch: Expects two random-access iterators which denote a sorted half-open range [begin, end), the item being searched for, and a comparator that is a strict weak ordering on T
    template <typename Iter, typename T, typename Comp>
    Iter binsearch(Iter begin, Iter end, T item, Comp comp, Iter nil) {
        typename std::iterator_traits<Iter>::difference_type length = end - begin;
        Iter mid = begin + length / 2;
        switch(length) {
        case 0:
            return nil;
        case 1:
            if(!comp(item, *mid) && !comp(*mid, item))
                return mid;
            else
                return nil;
        default:
            if(comp(item, *mid))
                return binsearch(begin, mid, item, comp, nil);
            else
                return binsearch(mid, end, item, comp, nil);
        }
    }
    
    template <typename Iter, typename T, typename Comp>
    Iter binsearch(Iter begin, Iter end, T item, Comp comp = Comp()) {
        return binsearch(begin, end, item, comp, end);
    }
    

    Compiled, but not tested.

  442. Did it in about 30 minutes while watching TV. As far as I know, it’s bug-free.

    import sys
    input = [1,2,4,5,6]
    T = 4 if len(sys.argv) == 1 else int(sys.argv[1])
    print "T = %s" % (T,)
    start, end = 0, len(input)-1
    while start != end:
        mid = start + int(round((float(end) - start)/2))
        if input[mid] > T:
            end = mid - 1
        else:
            start = mid
    if input[start] == T:
        print start
    else:
        print "NOT FOUND"
    
  443. forgot to put the source-meta stuff in, now ok. I tested meanwhile and know I belong to the 90%.

    ; return list starting after <start> end ending at <end>
    (defun sublist (lst start end)
      (butlast
       (nthcdr start lst)
       (- (length lst) end)
       )
      )
    
    ; find key key in list lst
    (defun binsearch( key lst )
      (let ((middle (/ (length lst) 2))
            )
        (progn
              (if (< key (nth middle lst))
                  (binsearch key (sublist lst 0 middle ))
                (if (> key (nth middle lst))
                    (binsearch key (sublist lst middle (length lst)))
                  (if (equal key (nth middle lst))
                      key
                    nil)
                  )
                )
          )
        )
      )
    
    
  444. Andrew Molyneux

    I thought I’d give it a crack in C#.
    Disclaimer 1: I’m pretty good in C and C++ but I have no serious experience with C#.
    Disclaimer 2: I’ve walked myself through this code in my head but I haven’t tested it.

    The BinarySearch() function returns the index of the item if found, otherwise -1.

    static long BinarySearch(int[] array, int item)
    {
        long first = 0;
        long last = array.Length - 1;
        while (first <= last)
        {
            long index = first + (last - first) / 2;
            if (array[index] == item)
            {
                return index;
            }
            if (array[index] > item)
            {
                last = index - 1;
            }
            if (array[index] < item)
            {
                first = index + 1;
            }
        }
        return -1;
    }
    
  445. I failed. My version ends up in an infinite loop if the value I’m searching for is larger than all values in the array, or if there are only two values in the array and the searched-for value is between those two.

    import sys, math
    
    val = int(sys.argv[1])
    data = map(int, sys.argv[2:])
    
    print "Searching for %s in %s" % (val, data)
    
    def bsearch(v, l, m): 
        #import pdb; pdb.set_trace()
        if(len(l) == 0): 
            return
        p = int(math.floor(len(l) / 2)) 
        b = l[p]
        if(v == b): 
            return p + m 
        if(v > b): 
            return bsearch(v, l[p:], m + p)
        return bsearch(v, l[:p], m)
    
    print "Result: %s" % bsearch(val, data, 0)
    
  446. Andrew Molyneux

    Having tested my code, it seems to be correct as far as I can tell. The only thing I missed is the possibility that the array reference passed can be null. I’d treat that as a precondition, so I’d insert the following line at the start of the function:

    Debug.Assert(array != null);
    
  447. no check…

    I’m gonna test it after posting.
    I’m pretty scared of the result though.

    {source}
    def binary_search(x,v):
    def aux(a,b):
    if a==b:
    return a if v[a]==x else None
    else:
    p = (a+b+1)/2
    if v[p]>x:
    return aux(a,p)
    else:
    return aux(p,b)
    return aux(0,len(v))
    {/source}

  448. Daniel Binau

    I’m kind of a n00b who only knows how to “paste libraries together” in C#, so it was fun to do this exercise and feel like a real programmer :-D

    I had two errors in my original attempt, both of which were easy to correct when I ran a simple test.

    Here is the corrected code, with comments about my original errors.

    It is probably not a very elegant implementation, but it seems to work.

    public static int? SearchSortedArray(int[] ary, int n)
            {
                return SearchSortedArray(ary, n, 0, ary.Length - 1);  // Original error: Forgot to subtract 1 from ary.Length
            }
    
            private static int? SearchSortedArray(int[] ary, int n, int fromIndex, int toIndex)
            {
                if (fromIndex < 0 || toIndex >= ary.Length)
                {
                    return null;
                }           
                
                if (fromIndex == toIndex)
                {
                    if (ary[fromIndex] == n)
                    {
                        return fromIndex;
                    }
                    else
                    {
                        return null;
                    }
                }
    
                int mid = (int)(fromIndex + toIndex) / 2 - (1/2); // Original error: Subtracted 1 instead of (1/2), resulting in an endless loop when toIndex - fromIndex == 2
    
                int test = ary[mid];
    
                if (test > n)
                {
                    return SearchSortedArray(ary, n, fromIndex, mid - 1);
                }
                if (test < n)
                {
                    return SearchSortedArray(ary, n, mid + 1, toIndex);
                }
    
                return (int?)mid;
            }
    
  449. André Silva
    
    
    public class binarySearch {
    
    	public static int binary_search(int[] array, int element) {
    		
    		return binary_search(0, array.length, array, element);
    	}
    	
    	public static int binary_search(int lower_bound, int upper_bound, int[] array, int element) {
    		
    		int middle = (upper_bound + lower_bound) / 2;
    		
    		if(lower_bound == middle || upper_bound == middle)
    			return -1;
    		
    		if(array[middle] < element)
    			return binary_search(middle, upper_bound, array, element);
    		else if(array[middle] > element)
    			return binary_search(lower_bound, middle, array, element);
    		else if(array[middle] == element)
    			return middle;
    		
    		else return -1;
    	}
    	
    	public static void main(String[] args) {
    	
    		int array[] = {1,2,3,4,5,6,7,8,9,10};
    		int element = 0;
    		
    		int position = binary_search(array, element);
    		
    		System.out.println("Element " + element + " is at position " + position);
    	}	
    }
    
  450. Well, I first written it (10-20 minutes) and then read till end where you say “do not test”.

    I know myself, I make a lot of mistakes. So I always write software with help of testing and debugging.

    For this task, testing was enough, I did not use debugger.

    Warning! Super-ugly c# code below (written it in linqpad)

    var testArr = new int []{1,2,2,3,3,3,3,5};

    var found=false;
    var findVal=5;
    var endPos=testArr.Length;
    var startPos=0;
    int center=-1;
    if (testArr.Length >0)
    do {
    var prevCenter=center;
    center=startPos+(endPos-startPos)/2;
    var curVal=testArr[center];
    if (curVal==findVal) {
    break;
    }
    if (prevCenter==center){
    center=-1;
    break;
    }
    if (curVal findVal){
    endPos=center;
    }

    }
    while (!found);

    (center).Dump();

  451. Scheme version, did no changes after testing it.

    (define (bsearch item vec)
    (define (iter start end)
    (if (> start end)
    -1 ; search failed
    (let* ((middle (quotient (+ start end) 2))
    (mid-val (vector-ref vec middle)))
    (cond ((= item mid-val) middle)
    ((< item mid-val) (iter start (- middle 1)))
    (else (iter (+ middle 1) end))))))
    (iter 0 (- (vector-length vec) 1)))

  452. Walter Stevens
    public class BSearch {
    
    	public static int search(int[] sortedArray, int target) {
    		return search(sortedArray, target, sortedArray.length/2, sortedArray.length, 0);
    	}
    	
    	private static int search(int[] sortedArray, int target, int current, int high, int low) {
    		if (sortedArray[current] == target)
    			return current;
    		if (sortedArray[current] > target) { //Too high, cut out the top half
    			if (current > low)
    				return search(sortedArray, target, goDown(current, low), current, low);
    			return -1;
    		}
    		
    		if (sortedArray[current] < target) { //Too low, cut out the bottom half
    			if (current < high)
    				return search(sortedArray, target, goUp(current, high), high, current);
    			return -1;
    		}
    		
    		return -1;
    	}
    	
    	private static int goUp(int current, int high) {
    		int goUpTo = ((current+high)/2);
    		if (goUpTo == current)
    			return current++;
    		return goUpTo;
    	}
    	
    	private static int goDown(int current, int low) {
    		int goDownTo = ((current+low)/2);
    		if (goDownTo == current)
    			return current--;
    		return goDownTo;
    	}	
    }
    
  453. David Mihola

    As far as I can see, this seems to work. Written according to the rules.

    import java.lang.Math;
    
    public class BinarySearchTest
    {
        public static void main(String[] args)
        {
            int[] a = {0};
            System.out.println(bsearch(a, 1));
    
            a = new int[] {0};
            System.out.println(bsearch(a, 0));
    
            a = new int[] {0, 1};
            System.out.println(bsearch(a, 1));
    
            a = new int[] {0, 1};
            System.out.println(bsearch(a, 0));
    
            a = new int[] {0, 1, 2, 3, 4};
            System.out.println(bsearch(a, 3));
    
            a =  new int[] {0, 1, 2, 3, 4};
            System.out.println(bsearch(a, 5));
        }
    
        public static int bsearch(int[] a, int key)
        {
            int first = 0;
            int last = a.length - 1;
    
            int middle;
    
            int foundAt = -1;
            
            while (first <= last)
            {
                middle = (first + last) / 2;
    
                if (key < a[middle])
                {
                    last = middle - 1;
                }
                else if (key > a[middle])
                {
                    first = middle + 1;
                }
                else if (key == a[middle])
                {
                    foundAt = middle;
                    first = last + 1;
                }
                else
                {
                    System.out.println("Error!");
                }
    
            }
    
            return foundAt;
            
        }
    }
    
  454. Daniel Binau

    I tested the code I posted earlier. Searching an array of all the numbers between 0 and 100 million that are divisible by 3 only takes a couple of seconds and gives the correct result.

    If I increase that number to 1 billion then I get memory overflow. This could of course be fixed by making it non-recursive but I’m too lazy for that :-)

  455. Just for kicks, I ran through the first 100 comments or so and tallied the language choice:

    Python 40
    C/C++ 36
    Unknown/pseudocode 6
    Lisp/Clojure/Scheme 5
    PHP 4
    Three each: Java, Perl, C#, JavaScript
    Haskell 2
    One each: VB, Delphi, Smalltalk, FORTRAN, Lua, Objective-C, ColdFusion

    Conclusion: Almost everyone (who cares about implementing binary search, anyway) uses C/C++ or Python.

    BTW, Google had me do this in my phone interview (reading code over the phone = not fun). I made the same off-by-one error as several commenters (< instead of <=) but caught it a few minutes later while we were still on the phone.

  456. I *think* my first version works:

    int binSearch(int[] arr, int n) {
    	int first = 0;
    	int last = arr.length - 1;
    	while (first <= last) {
    		//int mid = first/2 + last/2 + (((first % 2 != 0) && (last % 2 != 0)) ? 1 : 0); // This is the correct version, but more complicated
    		int mid = (first + last) / 2; // This is the simplified version, but doesn't work for very large numbers
    		int found = arr[mid];
    		if (found == n) {
    			return mid;
    		} else if (found < n) {
    			first = mid + 1;
    		} else { // found > n
    			last = mid - 1;
    		}
    	}
    	return -1;
    }
    
  457. Failed… just. :-)

    Firstly, I coded an inelegant recursive solution, and only when I started writing the test cases I realised I forgot the case of an empty array and a null array.

    For valid sorted arrays, however, it worked just fine, including searching for an item not in the array (returning -1).

  458. I made three mistakes.Here is my orginal code.
    {source}

    public class A{

    public static void main(){

    search(7);
    search(40);

    }

    public boolean search(final int n){
    int[] a = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
    int middle;
    int left=0;
    int right=a.length-1;
    for(;leftmiddle){
    left = middleindex+1;
    }else {
    right= middleindex-1;
    }
    }
    System.out.println(“Search “+n+”but result not found “);
    return false;
    }
    }
    {source}
    As you can see,main() function didn’t take paraments; forgot to make search() function static; and middleindex should be (right+left)/2, that was a clerical error.
    Please let me know if any other bug exists.

  459. I literally jumped in writing that code right when I read “try it” :-D

    I ended up with this and it seems to work

    function binSearch($sterm, $array) {
    	sort($array);
    	$result = 0;
    	while(true) {
    		if(count($array)<=0)
    			return false;
    		$j = round(count($array)/2)-1;
    		$tmp = array($sterm, $array[$j]);
    		sort($tmp);
    		if($tmp[0] == $tmp[1]) {
    			$result += $j;
    			return $result;
    		} else if($sterm == $tmp[0]) {
    			array_splice($array, $j, count($array)-$j);
    		} else {
    			array_splice($array, 0, $j+1);
    			$result += $j+1;
    		}
    	}
    }
    

    basically it quite mimics array_search() but without implementing it’s $strict parameter

  460. damn it! again:
    {source}

    public class A{

    public static void main(){

    search(7);
    search(40);

    }

    public boolean search(final int n){
    int[] a = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
    int middle;
    int left=0;
    int right=a.length-1;
    for(;leftmiddle){
    left = middleindex+1;
    }else {
    right= middleindex-1;
    }
    }
    System.out.println(“Search “+n+”but result not found “);
    return false;
    }
    }
    {/source}

  461. this one works fine; second try though, elisp by the way

    ; return list starting after <start> end ending at <end>
    (defun sublist (lst start end)
      (butlast
       (nthcdr start lst)
       (- (length lst) end)
       )
      )
    ; find key key in list lst
    (defun binsearch( key lst )
      (let ((middle (/ (length lst) 2))
            )
        (progn
          (if (not (equal lst nil))
              (if (< key (nth middle lst))
                  (binsearch key (sublist lst 0 middle ))
                (if (> key (nth middle lst))
                    (binsearch key (sublist lst middle (length lst)))
                  (if (equal key (nth middle lst))
                      key
                    nil)
                  )
                )
            nil)
          )
        )
      )
    
    
  462. Python, success, I definitely thought about it a lot harder when I couldn’t test.

    def binary_search(array, elem):
    l, r = 0, len(array)-1
    while True:
    m = l + ((r-l)//2)
    if array[m] == elem:
    return True
    elif r == l:
    return False
    elif array[m] > elem:
    r = m
    else:
    l = m + 1

  463. Pingback: 你是那10%可以写出二分查找的程序员吗? » 为之漫笔

  464. I can’t resist posing the evil question: if your array can be in size up to Integer.MAX_VALUE (for Java, but applies elsewhere as well), are you sure your code will not create integer overflows in your arithmetic? The code is only correct if it works for any arbitrary array being passed in…

  465. David Mihola

    @Martin Probst:

    You are obviously right. Count me in with the 90% then…

    but @Avi:

    I think your correct solution is unnecessarily complicated; someone else further up wrote something like this:

    middle = first + ( (last – first) / 2);

    This should also avoid the problem of integer overflows, shouldn’t it?

  466. @Martin Probst: at least mine shouldn’t
    @EoghanM: shouldn’t you return the position instead of just “yeah, found it in there”? :-)

  467. whoops, fell into the wordpress markup trap! Once again…

    function binarySearch(array, target) { //20, 19
      var length = array.length;
      var start = 1;
      var end = length;
      while(start <= array.length && start <= end) {
        if(target == array[end-1]) {
          return end-1;
        } else if (target == array[start-1]) {
          return start-1;
        }
        if(target > array[end-1]) {
          start = end + 1;      
          end += Math.round(length/2);
        } else { // target < array[end]
          end -= Math.round(length/2);
        }
        length = end - start + 1;
      }
      return -1; // not found
    }
    
  468. My code was correct.

  469. Here’s mine (recursive in Objective-C): http://gist.github.com/372377

    Time taken: about 20 minutes.
    I haven’t found any errors in my casual testing.

  470. Oh and here’s how you might test my earlier C++ binsearch:

    #include <vector>
    #include <iostream>
    #include <functional> // std::less
    #include <algorithm> // std::sort
    #include <ctime> // std::time
    #include <cstdlib> // std::rand, std::srand
    
    int main() {
        std::vector<int> v;
        std::srand(std::time(0));
        for(int i = 0; i < 20; ++i)
             v.push_back(std::rand() % 60);
    
        std::sort(v.begin(), v.end());
    
        int val;
    
        while(std::cout << "Enter value (any non-numeric input to quit): ", std::cin >> val) {
            std::vector<int>::iterator iter = binsearch(v.begin(), v.end(), val, std::less<int>());
            if(iter == v.end()) {
                std::cout << "Value (" << val << ") not found!" << std::endl;
            }
            else {
                std::cout << "Found value (" << val << ") at index " << iter - v.begin() << "." << std::endl;
            }
        }
    
        char const* c = "[";
        for(std::vector<int>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
            std::cout << c << *i;
            c = ", ";
        }
        std::cout << "]" << std::endl;
    }
    

    I also posted a little bit about the methodology here: http://www.reddit.com/r/programming/comments/bt7nh/according_to_jon_bentleys_book_programming_pearls/c0oh6po

    This methodology comes from (or rather, where I learned it from is) the C++ standard algorithms library, which is well worth examining because it has stellar design notwithstanding the ugliness of the language itself. The basic principles are applicable in most languages.

  471. dawid joubert

    Tested it, worked well for finding values but blew up trying to find value 200.

    #!/usr/local/bin/perl
    
    use strict;
    
    my @data = qw(1 2 5 6 7 9 10 13 19 20 200);
    my $left_index = 0;
    my $right_index = $#data;
    my $target = 200;
    
    
    LOOP:
    if ($left_index - $right_index == 0) {
    	goto NOT_FOUND;
    }
    
    my $index = ($left_index+$right_index) / 2;
    my $mid = $data[$index];
    
    print "Grabbed value $mid at index $index\n";
    
    if ($mid == $target) {
    	goto FOUND;
    }
    
    if ($mid < $target) {
    	$left_index = $index;
    }
    
    if ($mid > $target) {
    	$right_index = $index;
    }
    
    goto LOOP;
    
    	die "WTF Fall through";
    
    
    FOUND:
    	die "The target has been found\n";
    
    NOT_FOUND:
    	die "The target has not been found\n";
    
    
  472. I failed, with a simple and easily found error, but I did it when I read “try it” in the quoted section, not your later challenge. So I assumed that testing was allowed, and stopped to do so early. Trying to write perfect code straight out of the blue seems pretty pointless. Some people even say you should write thorough tests first and then code until they pass. The scope of this example is about as big as you can get and hope to have any success just doing it all in your brain, so what’s the point?

  473. Works for my test cases first try. I know it’s a bit ugly but no one said write elegant or pretty code.

    int bsearch(int * arr, int len, int val)
    {
    	int start = 0, end = len-1;
    
    	if(arr == 0 || len < 1)
    	{
    		return -1;
    	}
    	else if(len == 1)
    	{
    		if(arr[0] == val)
    		{
    			return 0;
    		}
    		else
    		{
    			return -1;
    		}
    	}
    
    	do
    	{
    		int mid = start + (end - start)/2;
    		
    		if(mid == start)
    		{
    			if(arr[start] == val)
    			{
    				return start;
    			}
    			else if(arr[end] == val)
    			{
    				return end;
    			}
    			else
    			{
    				return -1;
    			}
    		}		
    
    		if(mid > end || mid < start)
    		{
    			return -1;
    		}
    		else if(arr[mid] == val)
    		{
    			return mid;
    		}
    		else if(arr[mid] > val)
    		{
    			end = mid;
    		}
    		else
    		{
    			start = mid;
    		}
    	}while(start != end);
    
    	return -1;
    }
    
    
  474. I failed. That is all.

  475. First pass, buggy. Tried to be smart and got the sort condition backwards. Added one comprehensive test, fixed the bug. search(array, e)==i for all e=array[i] in a random, sorted array.

    That said, coding without tests is just trouble waiting to happen. Doing so is part of my standard work routine, and explicitly specifying “no tests” pushes this firmly into the realm of theoretical uselessness, even for a random exercise.

  476. 
    This was done in matlab
    
    array=[1:.5:100];
    T=0;
    [m,lenA]=size(array);
    pos=floor(lenA/2);
    ubound=lenA;
    lbound=array(1);
    
    S=input('What to search for?: ');
    
    while (T==0)
        if (S>array(pos))
            lbound=pos;
            pos=lbound+abs(floor((ubound-pos)/2));
        elseif (S<array(pos))
            ubound=pos;
            pos=abs(ceil((pos-lbound)/2));
        end
        
        if (S==array(pos))
            T=array(pos);
            break;
        end
        
        lbound;
        ubound;
        pos;
        
        if (abs(lbound-pos)<=1)||(abs(ubound-pos)<=1)
            T=-1;
            break
        end
    end
    
    if T==-1
        fprintf('S was not found in the array');
    else
        fprintf('S was found at location %i in the array', pos); 
    end
    
    
  477. 9 minutes

    use warnings;
    use strict;
    use POSIX qw(floor);

    use Test::More qw(no_plan);

    sub bsearch {
    my ($list, $target) = @_;

    return unless @$list;

    my $middle_index = floor($#$list / 2);
    my $middle_val = $list->[$middle_index];

    if ($middle_val == $target) {
    return $middle_val;
    }
    elsif ($middle_val < $target) {
    return bsearch([@$list[$middle_index + 1 .. $#$list]], $target)
    }
    else {
    return bsearch([@$list[0 .. $middle_index – 1]], $target)
    }
    }

    for (1..10) {
    ok(bsearch([1 .. 10], $_))
    }

    ok(!bsearch([1 .. 10], 0));
    ok(!bsearch([1 .. 10], 11));

  478. on my code, i wrote it to only work for integers, it could be modified to work for floats too

    also, i got it on probably my 4th or 5th run-through when debugging

  479. I didn’t get it quite right on the first go because of a stupid mistake – in the elif/else brackets, I’d originally just written “binsearch(…)” instead of “return binsearch(…)”. Other than that it appears to work.

    # unoptimized
    def binsearch(lst, val):
        if len(lst) == 1:
            if lst[0] == val:
                return True
            else:
                return False
    
        mid = len(lst)/2
        if lst[mid] == val:
            return True
        elif val < lst[mid]:
            return binsearch(lst[:mid], val)     else:
            return binsearch(lst[mid:], val) 
    
  480. Wow, from a quick audit it seems that mis-handling empty arrays is by far the most common mistake. Eg from a sampling of the solutions stated to be correct, I found it in 8 solutions (@finsprings, @Aaron, @Mike J, @Langtree, @Luke, @Marcus, @cycojesus, @Erik Swanson), with only 1 having a different mistake (@donaq, which was caught later).

  481. Success first time with Python.

    def binSearch( array, item ):
    low = 0
    high = len(array)
    while (high>low):
    mp = (high+low)/2
    if (array[mp].key == item): break
    if (array[mp].key low):
    return array[mp]
    return None

  482. Great work, Eric! I thought we might see a bit more of people pointing out bugs in each others’ code, but it’s nice to see that you weren’t satisified with destruction-testing just one solution :-)

    And, hey, the rest of you — you’re not going to take this lying down, are you? :-) Can someone find a bug in Eric’s submission? It’s at
    reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/#comment-1869

  483. Joe SixPacks
    my $SearchFor = 5;
    
    my @List = (1,2,3,4,5,6,7,8,9,10);
    my $LowRange = 1 - 1;
    my $HighRange = 10 - 1;
    
    my $Index = -1;
    
    while (($HighRange - $LowRange) > 1)
    {
      my $MidRange = $LowRange + int(($HighRange - $LowRange) * 0.5);
      
      if ($List[$LowRange] == $SearchFor)
      {
        $Index = $LowRange;
        last;
      }
      elsif ($List[$HighRange] == $SearchFor)
      {
        $Index = $HighRange;
        last;
      }
      elsif ($List[$MidRange] == $SearchFor)
      {
        $Index = $MidRange;
        last;
      }
      elsif ($List[$MidRange] > $SearchFor)
      {
        $HighRange = $MidRange;
      }
      else
      {
        $LowRange = $MidRange;
      }
    }
    
    if ($Index == -1)
    {
      if ($List[$LowRange] == $SearchFor)
      {
        $Index = $LowRange;
      }
      elsif ($List[$HighRange] == $SearchFor)
      {
        $Index = $HighRange;
      }
    }
    
    if ($Index != -1)
    {
      print "\n$List[$Index] is in $Index\n";
    }
    else
    {
      print "\nNot found.\n";
    }
    
  484. Ok – that code didn’t paste correctly – lost indentation and lost middle part of text.

    def binSearch( array, item ):
        low = 0
        high = len(array)
        while (high>low):
            mp = (high+low)/2
            if (array[mp].key == item): break
            if (array[mp].key low):
            return array[mp]
        return None
    
  485. {source}
    /**
    * if success,return the index of [toFind] in [_data]
    * else return -1.
    * @param _data source data
    * @param low start index of source data
    * @param high end index of source data
    * @param toFind number to find
    * @return
    */
    public static int search(int[] _data, int low, int high, int toFind){
    if(_data == null){
    return -1;
    }
    if(low > high){
    return -1;
    }
    if(low >= _data.length || high >= _data.length){
    return -1;
    }
    int mid = (low + high)/2;
    if(_data[mid] == toFind){
    return mid;
    }else if(_data[mid] > toFind){
    mid = mid – 1;
    return search(_data, low, mid, toFind);
    }else{
    mid = mid + 1;
    return search(_data, mid, high, toFind);
    }
    }
    {source}
    Only work for integers.
    Write by using JAVA.
    the potential bug may be found.

  486. I did this in ruby, and my first code was not correct. I had an infinite loop because I was not shrinking the size of my window.

    That said, the entire thing was coded, tested, corrected in less than 10 minutes, so I guess this is also a repeat of the “why is it important to be right first, as long as you are right at the end”. Testing is important for code.

  487. python:

    def bsearch(needle, haystack) :
    idx = len(haystack) / 2

    if idx == 0 :
    return -1

    if needle == haystack[idx] :
    return idx

    if needle haystack[idx] :
    return bsearch(needle, haystack[idx+1:]

    success! i think. it may lose in an edge case (like haystack of length 0) but that’s outside the scope of the algorithm and more an exercise in defensive programming IMO.

  488. def bsearch(needle, haystack)
      middle_ix = haystack.length / 2
    
      if haystack.empty?
        false
      elsif haystack[middle_ix] == needle
        true
      elsif haystack[middle_ix] < needle
        bsearch(needle, haystack[0...middle_ix])
      else
        bsearch(needle, haystack[(middle_ix + 1)..-1]
      end
    end
    

    Coded and pasted here with no testing.

    Third branch of the if compares in the wrong direction, so if the target is in the first half of the array, it would narrow to the second half. Whoops.

  489. I felt my heart sink when it threw an exception on first run, but it turned out the error was forgetting to sort the test arrays in the test code, so all was ok :)

    
    def bsearch(haystack, needle):
    	if len(haystack) == 0:
    		raise ValueError("Element not found")
    
    	mid = len(haystack) >> 1
    
    	if haystack[mid] == needle:
    		return mid
    	elif needle < haystack[mid]:
    		return bsearch(haystack[:mid], needle)
    	else:
    		return bsearch(haystack[mid+1:], needle) + mid + 1
    
    
    
    def test():
        import random
    
        random.seed()
    
        def create_test_data(length):
            values = []
            for i in range(length):
                values.append(random.randint(1, 1 << 30))
    
            return sorted(values)
    
        passes = 0
        fails = 0
    
        for run in range(20):
            length = random.randint(1,50)
            sample = create_test_data(length)
    
            for i, v in enumerate(sample[:]):
                try:
                    answer = bsearch(sample, v)
                except ValueError:
                    print "Ummm, couldn't find %i in %s" % (v, sample)
                    raise
    
                if answer != i:
                    print "Error. bsearch returned %i for %i in %s" % (answer, v, sample)
                    fails += 1
                else:
                    passes += 1
    
            # Test "not in array" case, including at least one test for a value too small and a value too big.
            tests = set(create_test_data(random.randint(1, 100))) - set(sample)
            tests.add(min(sample) - random.randint(1, 50))
            tests.add(max(sample) + random.randint(1, 50))
    
            for test in tests:
                try:
                    answer = bsearch(sample, test)
                except ValueError:
                    passes += 1
                except:
                    print "Error: threw an unexpected exception when attempting to find %i in %s" % (too_large, sample)
                    fails += 1
                else:
                    print "Error: found %i at position %i when it shouldn't have in %s" % (too_large, answer, sample)
                    fails += 1
    
    
        print "Passed %i Failed %i" % (passes, fails)
    
    

    After I wrote the code in an editor, I did do about 4 example runs-through (algorithm in my head; pen and paper for variable store) which was how i remembered to add 1 to mid for the slice and return in the RHS block.
    I’m not sure if that counts as cheating, but I’m pretty sure it doesn’t.
    10mins. and maybe 20-30 more for the test code.

  490. Failed. (Python)

  491. Failed on boundary case! (Only gave myself 15 minutes, but still…)

    def bsearch (haystack, lower, upper, needle):
    	if upper == lower:
    		return -1;
    	
    	key = (lower + upper) / 2
    	if haystack[key] == needle:
    		return key
    	elif needle < haystack[key]:
    		return bsearch(haystack, lower, key-1, needle)
    	elif haystack[key] < needle:
    		return bsearch(haystack, key+1, upper, needle)
    		
    def binarysearch(haystack, needle):
    	return bsearch(haystack, 0, len(haystack)-1, needle)
    		
    haystack = [10, 33, 45, 46, 54, 81, 93, 94, 100]
    needle=10
    binarysearch(haystack, needle)
    # Gave -1...
    
  492. Mine did not run the first time I ran it. Syntax error. Fixing the syntax error, I popped the stack.

    Crap.

  493. I’m way late to the game, but if you are indeed tallying the results, add me to the list. I assumed arguments would not be null. Up to you whether you consider that a bug or not for the purposes of this exercise.

    Recursive solution written in C#, in about an hour:

    
    	    /// <summary>
    	    /// Finds the given element in the given array.
    	    /// </summary>
    	    /// <returns>True if the element exists in the array.</returns>
    	    static bool IsFoundByBinarySearch(int[] arrayToSearch, int elementToFind)
            {
                if (arrayToSearch.Length <= 0)
                {
                    return false;
                }
    
                if (arrayToSearch.Length == 1)
                {
                    return (arrayToSearch[ 0 ] == elementToFind);
                }
    
                int[] splitArray = new int[ arrayToSearch.Length / 2 ];
    
                if (arrayToSearch.Length % 2 == 0)
                {
                    if (elementToFind.CompareTo(arrayToSearch[ arrayToSearch.Length / 2 - 1 ]) <= 0)
                    {
                        Array.Copy(arrayToSearch, 0, splitArray, 0, arrayToSearch.Length / 2);
                        return IsFoundByBinarySearch(splitArray, elementToFind);
                    }
    
                    Array.Copy(arrayToSearch, arrayToSearch.Length / 2, splitArray, 0, arrayToSearch.Length / 2);
                    return IsFoundByBinarySearch(splitArray, elementToFind);
                }
    
                if (elementToFind.CompareTo(arrayToSearch[ arrayToSearch.Length / 2]) == 0)
                {
                    return true;
                }
    
                if (elementToFind.CompareTo(arrayToSearch[ arrayToSearch.Length / 2]) < 0)
                {
                    Array.Copy(arrayToSearch, 0, splitArray, 0, arrayToSearch.Length / 2);
                    return IsFoundByBinarySearch(splitArray, elementToFind);
                }
    
                Array.Copy(arrayToSearch, arrayToSearch.Length / 2 + 1, splitArray, 0, arrayToSearch.Length / 2);
                return IsFoundByBinarySearch(splitArray, elementToFind);
            }
    
  494. Ryan,

    It is astounding, but yours is the 500th comment on this entry!

    It seems obvious to me from the problem description that you can assume the array reference is not null (i.e. there is actually an array!) but that zero is a perfectly good number of items for that array to contain. So programs that fail when passed a null Array are fine; programs that go crazy when asked to search and empty array fail the test, and diminish, and go into the west.

  495. Some of you guys can write such compact code!! I wrote mine before reading the ‘no testing’ rule so here goes:

    private void BinarySearch()
            {
                ulong[] numberList = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 
                                        2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 
                                        317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 
                                        14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 
                                        433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 
                                        7778742049, 12586269025, 20365011074 };
    
                ulong searchNum = 1346269;
    
                bool numFound = FindNumber(numberList, searchNum, numberList.Length - 1, 0);
                numFound = FindNumber(numberList, (ulong)47, 0, numberList.Length - 1);
                searchNum = 20365011074;
                numFound = FindNumber(numberList, searchNum, 0, numberList.Length - 1);
                
            }
    
            private bool FindNumber(ulong[] numberList, ulong searchNum, int listStart, int listEnd)
            {
                int midItem = listStart;
    
                if (listStart > listEnd)
                {
                    midItem = listEnd;
                    listEnd = listStart;
                    listStart = midItem;
                }
    
                int itemCount = (listEnd - listStart) + 1;
                
                if (itemCount > numberList.Length)
                {
                    midItem = listStart = 0;
                    listEnd = numberList.Length - 1;
                    itemCount = numberList.Length;
                }
    
                if (itemCount == 1)
                {
                    if (numberList[midItem] == searchNum)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
    
                if ((itemCount % 2) == 0)
                {
                    midItem = listStart + itemCount / 2 - 1;
                }
                else
                {
                    midItem = listStart + itemCount / 2;
                }
    
                if (numberList[midItem] == searchNum)
                {
                    return true;
                }
    
                if (numberList[midItem] > searchNum)
                {
                    return FindNumber(numberList, searchNum, listStart, midItem);
                }
                else
                {
                    return FindNumber(numberList, searchNum, midItem + 1, listEnd);
                }
            }
    
  496. Mike,

    Congrats on the new comment record :o)

    You seem to have struck a nerve with a lot of people. I know for one that I’ve learned something from participating.

    Thanks,
    Ryan

  497. I had an off-by-one error, a comparison flip, and I forgot to propagate the slice offset back up the call chain.

    function binary_search(array, value, offset) {	
    		var pivot_index = Math.floor(array.length / 2);
    		
    		var pivot = array[pivot_index];
    		
    		if (pivot == value)
    			return pivot_index + offset;
    		else if (array.length > 1) {
    			if (value < pivot)
    				return binary_search(array.slice(0, pivot_index), value, pivot_index);
    			else
    				return binary_search(array.slice(pivot_index, array.length), value, pivot_index);
    		} else
    			return null;
    	}
    

    The last argument need to be zero when called by a user.

  498. One more fix: the offset needs to be added to the pivot index in the recursive call.

  499. @Jeff: Handling a haystack of length 0 certainly IS within the scope of this problem. An array with no elements is sorted (see: vacuous truth), and so is valid input. Also, it doesn’t appear that your code handles arrays of length 1 either (because len(haystack)/2 == 0 in that case).

  500. Did it in *looks guilty* LabVIEW. Worked on first run. Fun post, thanks for the distraction!

  501. @David Mihola: Yes, it probably is too complicated. But since “For the purposes of this exercise, the possibility of numeric overflow in index calculations can be ignored”, I didn’t worry about cleaning it up too much – I just copied what I originally wrote.

  502. my cheat: I did this last week to demonstrate the principle to a friend. No copy+paste, though. All from my head. This is probably not the fastest way to do it.

    def bsrch( d, target ) :
    if len(d) is 1 and d[0] != target:
    return None
    print(“Searching “, d, ” for “, target)
    midpoint = len(d)/2
    value = d[midpoint]
    if target == value:
    return midpoint
    else:
    if value > target:
    rval = bsrch( d[:midpoint], target )
    if rval != None:
    return rval
    else:
    rval = bsrch( d[midpoint:], target )
    if rval != None:
    return rval + midpoint

  503. Untested and took about 15 minutes.

    {source}public static int search(List sortedHaystack, String needle) {
    int startRange = 0;
    int endRange = sortedHaystack.size() – 1;

    while (endRange – startRange >= 0) {
    int midRange = startRange + ((endRange – startRange) / 2);
    String value = sortedHaystack.get(midRange);
    int comparison = value.compareTo(needle);
    if (comparison == 0) {
    return midRange;
    } else if (comparison < 0) {
    startRange = midRange + 1;
    } else {
    endRange = midRange – 1;
    }
    }

    throw new IllegalStateException("Unable to find needle.");
    }
    {/source}

  504. public class BinarySearch {
    
    	public static void main(String []args){
    		
    		int[] foo = {1,2,3,4,5,6,7,8,9,10};
    		 System.out.println("position:"+search(foo,0,foo.length,4) +" value:4");
    		 System.out.println("position:"+search(foo,0,foo.length,6) +" value:6");
    		 System.out.println("position:"+search(foo,0,foo.length,9) +" value:9");
    		 System.out.println("position:"+search(foo,0,foo.length,12) +" value:12");
    		 System.out.println("position:"+search(foo,0,foo.length,0) +" value:1");
    	}
    	
    	public static int search(int[] range, int min, int max, int value){
    		int mid = ((max - min)/2) + (min);//half of range offset by min
    		if (min >= max)
    			return -1;//not found		
    		else if(value == range[mid])
    			return mid;
    		else if (value < range[mid]){
    			max = mid - 1;
    			return search(range,min,max,value);
    		}
    		else{
    			min = mid + 1;
    			return search(range,min,max,value);
    		}
    	}
    }
    
    
  505. Maybe 8 mins in PHP and running it in my head. Not proud of it in parts, and still haven’t tested it. My ego is afraid to…

    function bsearch($haystack, $needle, $lower_bound = 0, $upper_bound = -1) {
    	if (count($haystack) == 0) {
    		return -1;
    	}
    
    	if ($upper_bound == -1) {
    		$upper_bound = count($haystack) - 1;
    	}
    
    	$mid = $lower_bound + floor(($upper_bound - $lower_bound) / 2);
    	
    	if ($haystack[$mid] == $needle) {
    		return $mid;
    	} else if ($haystack[$mid] < $needle) {
    		$upper_bound = $mid - 1;
    	} else if ($haystack[$mid] > $needle) {
    		$lower_bound = $mid + 1;
    	}
    
    	if ($lower_bound <= $upper_bound) {
    		bsearch($haystack, $needle, $lower_bound, $upper_bound);
    	}
    
    	return -1;
    }
    
  506. but I should read the instructions, sorry for the html mess:

    	/**
    	 * binary search algorithm
    	 * @param array which must be strictly ascending and must not contain null values
    	 * @param toSearch
    	 * @return index of the searched object or -1 if it wasn't found
    	 */
    	public static <T extends Comparable<T>> int binarySearch(T[] array, T toSearch) {
    		if (toSearch==null) {
    			throw new NullPointerException("toSearch must not be null");
    		}
    		if (array==null || array.length==0) {
    			return -1;
    		}
    		return binarySearchIntern(array,toSearch,0,array.length-1);
    	}
    
    	private static <T extends Comparable<T>> int binarySearchIntern(T[] array, T toSearch, int l, int r) {
    		int m = (l+r)/2;
    		int compare = toSearch.compareTo(array[m]);
    		if (compare==0) {
    			return m;
    		} 
    		if (l<r) {
    			if (compare<0) {
    				if (l<m) {
    					return binarySearchIntern(array, toSearch, l, m-1);
    				}
    			} else {
    				return binarySearchIntern(array, toSearch, m+1, r);
    			}
    		}
    		return -1;
    	}
    
  507. argh, right after posting it, I can see I pasted the wrong one. Middle should be:

    	} else if ($haystack[$mid] > $needle) {
    		$upper_bound = $mid - 1;
    	} else if ($haystack[$mid] < $needle) {
    		$lower_bound = $mid + 1;
    	}
    
  508. He, not even interested in trying – it’s a sheer bullshit metric. I doubt that my mechanic is in the 0.1% that can take my tires off without a wrench, either, just using his bare hands.

    ;)

  509. Ok, well, I haven’t been able to actually test this and it’s been a while since I’ve written C but here we go:

    http://pastebin.org/160265

    So, that was done in Notepad++ and I’m yet to compile it. Fingers crossed :)

    Now to look at some of the answers :)

    Andrew

  510. David Mihola

    @Avi: I see. And just in case: I didn’t mean to sound offensive – my intention was more to check if I was right than to tell you that your solution could be better – at least yours was correct whereas mine wasn’t…

  511. Here is my implementation in Perl. I tested it with a simple lexically-sorted array (‘a’..’z’) and it seems to work. It will probably fail horribly with numerically-sorted arrays.

    # returns index of first item found in array that matches term, or undef if not found
    sub bsearch {
      my($term, $array, $start, $end) = @_;
    
      my $middle = int(($start + $end) / 2);
    
      if ( ($term lt $array->[$start]) or ($term gt $array->[$end]) or ($start > $end) ) {
        return undef;
      }
      elsif ($term lt $array->[$middle]) {
        $end = $middle - 1;
      }
      elsif ($term gt $array->[$middle]) {
        $start = $middle + 1;
      }
      elsif ($term eq $array->[$middle]) {
        return $middle;
      }
      else {
        die "ERROR: s = $start, e = $end, m = $middle, t = $term, item = " . $array->[$middle] ;
      }
    
      bsearch($term, $array, $start, $end);
    
    }
    

    Here is an example of how to call it:

    # lexically-sorted array
    my @array = ('a'..'z');
    
    my $term = $ARGV[0] || 'e';
    
    my $r = bsearch($term, \@array, 0, $#array);
    
    if (defined $r) {
      print "Found: $term = " . $array[$r] . "\n";
    }
    else {
      print "term '$term' not found\n";
    }
    
  512. def search(n,h):
      return search2(n,h,0,len(h)-1)
    
    def search2(n,h,low,high):
      if low > high:
        return None
      l = high-low+1
      p = l/2 + low
      pe = h[p]
      if pe == n:
        return pe
      elif pe < n:
        return search2(n,h,p+1,high)
      return search2(n,h,low,p-1)
    

    Testing didn’t reveal any bugs, if anyone finds a bug I’d be most interested in hearing it…

  513. Jason Spencer

    Decided to have another look….FAIL. Mine dies if the search is larger than the largest item in the array.

    We should go back to the essence of this blog post. I think Bentley’s point was that after explaining the algorithm in english it sounds easy to write. Easy enough that any real programmer could probably spit out a working implementation without actually trying it. His point is that this is deceptive; the code is trickier than the explanation.

    Perhaps because we’re plugging other peoples’ libraries together so often, we’re used to black boxes. I think that’s why so many people are appalled by the idea of not testing. Testing is quicker and easier than reading the source of the libraries you’re using.

  514. @acastle: In your recurse I think it should be RETURN bsearch(…, but other than that your corrected version looks ok.

  515. @Tristain: You are using max to be exclusive, correct? Then in the recurse when value < range[mid], you should not use mid-1 as the new max or you skip the index before mid as well. E.g. try adding a test case to search for 5 in your array.

  516. Guilherme Melo

    Manually tested with a few values and lists.

    def binary_search(a_list, elem):
    	if not a_list:
    		return False
    	index = len(a_list)/2
    
    	ret = cmp(elem, a_list[index])
    	
    	if ret < 0:
    		return binary_search(a_list[0:index], elem)
    	elif ret > 0:
    		return binary_search(a_list[index + 1:len(a_list)], elem)
    	else:
    		return True
    
  517. Untested but it should work

            public static int bsearch (int value, int[] data) {
    	        if (data.Length == 0)
    		        return -1;
    	        return bsearch (value, data, 0, data.Length - 1);
            }
    
            static int bsearch (int value, int[] data, int lowerIndex, int upperIndex) {
    	        if (data [lowerIndex] > value)
    		        return -1;
    	        if (data [upperIndex] < value)
    		        return -1;
    	        if (lowerIndex == upperIndex) {
    		        if (data [lowerIndex] == value)
    			        return lowerIndex;
    		        else
    			        return - 1;
    	        }
    	        int pivot = lowerIndex + (upperIndex - lowerIndex) / 2;
    	        var result = bsearch (value, data, lowerIndex, pivot);
    	        if (result != -1)
    		        return result;
    	        if (pivot == upperIndex)
    		        return -1;
    	        return bsearch (value, data, pivot + 1, upperIndex);
            }
    
  518. I actually ended up doing a little testing before I posted it. Only error I found was forgetting that python list slices are [s,e) and not [s,e].

    >>> 2 // 4
    0
    >>> 5 // 2
    2
    >>> 2 // 2
    1
    >>> 4 // 2
    2
    >>> 3 // 2
    1
    >>> my_list = [i for i in range(100)]
    >>>
    >>> def find_index_of_i(my_list, i):
    … s = 0
    … e = len(my_list) – 1
    … while True:
    … if len(my_list[s:e]) <= 2:
    … if my_list[s] == i: return s
    … elif my_list[e] == i: return e
    … else: return -1
    … middle = s + (e – s) // 2
    … if my_list[middle] == i: return middle
    … elif my_list[middle] i: e = middle

    >>> find_index_of_i(5)
    Traceback (most recent call last):
    File “”, line 1, in
    TypeError: find_index_of_i() takes exactly 2 arguments (1 given)
    >>> find_index_of_i(imy_list, 5)
    Traceback (most recent call last):
    File “”, line 1, in
    NameError: name ‘imy_list’ is not defined
    >>> find_index_of_i(my_list, 5)
    -1
    >>> 99 // 2
    49
    >>> 49 // 2
    24
    >>> 3 // 2
    1
    >>> my_list = [i for i in range(100)]
    >>>
    >>> def find_index_of_i(my_list, i):
    … s = 0
    … e = len(my_list) – 1
    … while True:
    … if len(my_list[s:e + 1]) <= 2:
    … if my_list[s] == i: return s
    … elif my_list[e] == i: return e
    … else: return -1
    … middle = s + (e – s) // 2
    … if my_list[middle] == i: return middle
    … elif my_list[middle]
    i: e = middle

    >>> find_index_of_i(my_list, 5)
    5
    >>> find_index_of_i(my_list, 10)
    10
    >>> find_index_of_i(my_list, 20)
    20
    >>> find_index_of_i(my_list, 21)
    21
    >>> [0, 1, 2, 3, 4, 5]
    [0, 1, 2, 3, 4, 5]
    >>> li = [0, 1, 2, 3, 4, 5]
    >>> li[0]
    0
    >>> find_index_of_i(my_list, 0)
    0
    >>> find_index_of_i(my_list, -30)
    -1

  519. Viktor Skarlatov
    int find(int *arr, int beg, int end, int num)
    {
        int half = beg + (end - beg) / 2;
        while(beg <= end)
        {
    	if(arr[half] == num)
    	{
    	    return half;
    	}
    
    	if(arr[half] > num)
    	{
    	    end = half - 1;
    	}
    
    	if(arr[half] < num)
    	{
    	    beg = half + 1;
    	}
    
    	half = beg + (end - beg) / 2;
        }
    
        return -1;
    }
    
  520. As I said, I’m confident that my solution works, i hope that I’m correct.

    Still it’s fair to note that i almost forgot a couple of details, for example, i almost left line 18 as int pivot = (upperIndex – lowerIndex) / 2; forgetting to add up lowerIndex again to the pivot, and line 22 was a nearly miss to, i almost forgot to check for the pivot becoming greater than the upperIndex when i add 1 to it on line 24.

    As they say, the devil is on the details :p

  521. Wait, “NO TESTING until after you’ve decided your program is correct.” ?

    I’m a Lisp programmer. There’s no such thing as “not testing”, since I test every little bit in the REPL as I go.

  522. Here’s a test harness for a Javascript function following the same contract as mine (which passed):

    function testSearch(find) {
        for (var length = 0; length < 10; ++length)
            for (var times = 100; 0 < times; --times) {
                var array = randomArray(length, length);
                array.sort();
                var key = randomInt(length+2) - 1;
                var j = find(array.slice(0), key);
                if (j === null)
                    assert(-1 === array.indexOf(key), "-1 === array.indexOf(key)");
                else {
                    assert(0 <= j, "0 <= j");
                    assert(j < length, "j < array.length");
                    assert(key === array[j], "key === array[j]");
                }
            }
    }
    
    function randomArray(length, range) {
        var array = [];
        for (var i = 0; i < length; ++i)
            array[i] = randomInt(range);
        return array;
    }
    
    function randomInt(range) {
        return Math.floor(range * Math.random());
    }
    
    function assert(ok, message) {
        if (!ok)
            throw new Error("Assertion failed: " + message);
    }
  523. André Silva

    So far, my implementation has passed all the tests (surprisingly!). I did this in about 8 minutes and to be honest I didn’t even consider some cases (e.g. empty arrays) but so far every test I did has passed. Also my implementation does not add +1 or -1 to the middle index, does anyone care to tell me if my implementation is “good”?

    
    public class binarySearch {
    
    	public static int binary_search(int[] array, int element) {
    		
    		return binary_search(0, array.length, array, element);
    	}
    	
    	public static int binary_search(int lower_bound, int upper_bound, int[] array, int element) {
    		
    		int middle = (upper_bound + lower_bound) / 2;
    		
    		if(lower_bound == middle || upper_bound == middle)
    			return -1;
    		
    		if(array[middle] < element)
    			return binary_search(middle, upper_bound, array, element);
    		else if(array[middle] > element)
    			return binary_search(lower_bound, middle, array, element);
    		else if(array[middle] == element)
    			return middle;
    		
    		else return -1;
    	}
    	
    	public static void main(String[] args) {
    	
    		int array[] = {1,2,5,6,7,9,10,13,19,20,200};
    		int element = 200;
    		
    		int position = binary_search(array, element);
    		
    		System.out.println("Element " + element + " is at position " + position);
    	}	
    }
    
  524. BTW it’s a bit boggling to see the solutions doing array slices. They’re O(n).

  525. Pingback: links for 2010-04-20 « that dismal science

  526. André: try binary_search({0}, 0).

  527. @Eric Good Call!

  528. André Silva

    Nice catch Darius. This test condition is wrong

    if(lower_bound == middle || upper_bound == middle)
    			return -1;
    
  529. Wrote it in Java, recursively. And got the recursion wrong the first time – but that’s what unit tests are for :)

        private int doBinSearch(final int[] data, final int lower, final int upper, final int search)
        {
            if (lower >= upper)
            {
                if (data[lower] == search)
                {
                    return lower;
                }
                return -1;
            }
            int middle = lower + (upper - lower) / 2;
            final int middleValue = data[middle];
            if (search == middleValue)
            {
                return middle;
            }
            if (middleValue > search)
            {
                return (doBinSearch(data, lower, middle - 1, search));
            }
            else
            {
                return (doBinSearch(data, middle + 1, upper, search));
            }
        }
    
    
  530. This is one of the reasons that I favour use of ready-made libraries: Trying to write an own sorting/searching/whatnot algorithm is a beginner’s error (in and by it self). There are well-tested and highly performant libraries for such basic functions available in any established high-level language—only rarely is the small gains possible by a custom made solution enough to justify writing an algorith from scratch.

    Even if such justification can be found (possibly writing the first such library for a newer language) the correct way is obviously to grab a good book on the topic, pick a suitable implementation, and (if needed) translate/modify it.

    More generally, in today’s software development, the main task at hand is limiting and controling complexity of various kinds. One way this is done is re-use: A less optimized solution with re-use is usually preferable to an optimized special-purpose solution. (Notwithstanding that some libraries and frameworks are too bloated to always be acceptable.) We can hope for the return of small Unix tools that each individually are of sufficiently low complexity that such measures are not needed; however, in reality, we are stuck with ever growing applications with ever increasing complexity—and, in today’s world, the good developer is not the one who understands complex code and algorithms, but the one who avoids or manages them. A sad, but near inevitable development.

    (I realize that this was likely not the point you were making.)

  531. Aww, what the heck …

    	;======================
    	; assume:
    	;    ecx = v
    	;    ebx = *p
    	;    edi = p.size
    	;    esi = 0
    	;---------------------------------------
    _loop:
    	cmp    esi, edi			
    	jz     _quit
    	;---------------------------------------
    	mov    edx, edi			
    	sub    edx, esi			
    	shr    edx, 1				
    	add	    edx, esi			
    	;---------------------------------------
    	cmp    ecx, [ebx + edx * 4] 	
    	jle    @F				
    	lea    esi, [edx + 1]			
    	jmp    _loop
    	;---------------------------------------
    @@:
    	mov    edi, edx			
    	jmp    _loop
    	;---------------------------------------
    _quit:
    	mov    eax, esi
    	cmp    ecx, [ebx + esi * 4]
    	jz     @F
    	mov    eax, -1
    @@:		
    	;======================
    
  532. So are these the bugs?

    A is empty.
    Can’t find T in certain places or certain sizes of A (even? odd? powers of 2?)
    Goes into an infinite loop sometimes. (How do you test for this?)
    Becomes a linear search sometimes (Did you test for this?)
    Tries to access off the end.
    Doesn’t return “not found” sometimes when T isn’t in A.
    T is less than the first or greater than the last element.
    Does (a+b)/2, which overflows with ints. (Did anyone here find this one by testing?)*

    *The last is a problem if you have 2^31 one-byte elements to search (it could happen!). Or would be if you were searching a function or disk file rather than an array. Or if you used 32-bit ints (not size_t) to index arrays in a 64-bit computer, which I think would be the more basic problem. Or, if you’re in Python 2.x not 3.x, overflow bumps you into long ints, and then your function returns a long int which could have cascading inefficiency effects :-(

  533. LOL @ Vince’s assembly version. Short, too!

    But, come on people — does no-one write COBOL any more? Or APL? Or ETA?

  534. Oops, forgot:
    Has a problem with duplicate entries.

  535. In Redcode :-)

              nop    <bot,           >size
    _bsnext   mov.b  size,           ptr
              div    #2,             ptr
              jmn    _bscont,        ptr
    
              mov.b  bot,            ptr
              jmp    notfound,       >ptr
    
    _bscont   add.b  bot,            ptr
              slt    @ptr,           find
              jmp    _bstopadj
    
              add.b  bot,            size
              sub.b  ptr,            size
              mov.b  ptr,            bot
              jmp    _bsnext
    
    _bstopadj sne.b  @ptr,           find
              jmp    found
    
              div    #2,             size
              jmp    _bsnext
    
  536. @vince: It doesn’t look like you’re handling 0-length arrays correctly? That is, if ecx == [ebx] when edi==0, 0 will be returned instead of -1. Assuming [ebx] doesn’t cause an exception in that case, that is.

  537. Now ETA would be fun. But Mike, the task is surely yours….

  538. I wrote the code in eclipse because it was already opened.
    I did not get it right in the first try, I executed the code once, found a bug, and fixed, so this is the second try and it works fine :D

    public class FinderTest {
    	static int[] origArr = {0, 3, 5, 7, 9, 11, 13, 14, 17, 24, 50, 57, 60, 90};
    
    	public static void main(String[] args) {
    		int vtf = Integer.valueOf(args[0]);
    		int pos = binarySearch(origArr, vtf, 0, origArr.length - 1);
    		System.out.println(pos);
    	}
    
    	private static int binarySearch(int[] arr, int vtf, int start, int end) {
    		int idx = start + ((end - start) / 2);
    		int cval = arr[idx];
    		if (cval == vtf)
    			return idx;
    		if (idx == start)
    			return -1;
    		if (cval > vtf)
    			return binarySearch(arr, vtf, start, idx - 1);
    		if (cval < vtf)
    			return binarySearch(arr, vtf, idx, end);
    		return -1;
    	}
    }
    
  539. Just found a bug in the code I posted, the code cannot find the last element of the array, to fix, just remove the “-1” from the main, and pass the array length there instead of the last position.

  540. Kaveh Shahbazian

    let rec binSearch target sortedArray =
    match sortedArray with
    | [||] -> None
    | _ ->
    let maidenIndex : int = sortedArray.Length / 2
    if target sortedArray.[maidenIndex] then binSearch target sortedArray.[(maidenIndex + 1)..(sortedArray.Length – 1)]
    else Some(sortedArray.[maidenIndex])

  541. Jeremy Cromwell
        static int find(int el, Integer list[])
        {
            int spos = 0;
            int epos = list.length;
            int cpos;
            while (spos < epos)
            {
                cpos = spos + ((epos-spos)/2);
                if (el == list[cpos]) return cpos;
                if (el < list[cpos])
                {
                    epos = cpos;
                }
                else
                {
                    if (spos == cpos) return -1;
                    spos = cpos;
                }
            }
            return -1;
        }
     

    Wrote it in about 20 minutes, then tested it and it works great even when the element isn’t in the list.

  542. Here’s my best shot in Python. I just wrote it by hand, so I might have messed up the syntax a bit (although it looks ok).

    search(val, data):
    L = len(data)
    mid = L / 2
    mid_val = data[mid]
    if val == mid_val:
    return mid
    if L <= 1:
    return None
    if val mid_val:
    search(val, data[mid + 1 : L])

  543. @eric: You’re right of course. the code doesn’t check for zero length arrays. [ebx] itself can’t cause an exception because ebx is a valid address on the heap, but nonetheless a bug’s a bug, and so take my seat among the 90%…

  544. Kaveh Shahbazian

    Didn’t work! And I have remembered gist: http://gist.github.com/373070

  545. Ah, I see. The WordPress bug. Sorry for being notorious. If now it won’t paste properly then I’m done!
    {source}
    import util.Random
    val toFind = args(0).toInt
    val r = new Random()
    val a = (for (i<- 1 to 100) yield r.nextInt(100)).toList.sort(_<_).toArray
    def binsearch(x:Int, a:Array[Int],first:Int, last:Int):Int = [
    if (last-first=x) return binsearch(x, a, first, mid)
    else return binsearch(x, a, mid, last)
    ]
    ]
    for (el<-0 to 99) println(el+":"+a(el))
    val res = binsearch(toFind, a, 0,99)
    println ("res="+res)

    {/source}

  546. Kaveh Shahbazian

    I meant posting some code here didn’t work! But my code worked! :-) So I’v decided to make a gist! (I was thinking along my previous entries! OOps!)

  547. public static int search(int[] data, int sample) {
        return doSearch(data, sample, 0, data.length);
    }
    
    private static int doSearch(int[] data, int sample, int from, int to) {
        if (from>=to) return -1;
        int middle = (from+to)/2;
        if (data[middle]==sample)
            return middle;
        if (data[middle]<sample)
            return doSearch(data, sample, middle+1, to);
        else
            return doSearch(data, sample, from, middle);
    }
    
  548. Let’s try once more. A post somewhere up above says to use {source}…{/source} but with square brackets. (Do you think he meant angle brackets?

    def binSearch( array, item ):
        low = 0
        high = len(array)
        while (high>low):
            mp = (high+low)/2
            if (array[mp].key == item): break
            if (array[mp].key < item):
               low = mp+1
            else:
               high = mp
        if (high>low):
            return array[mp]
        return None
    
  549. Arghhh I surrender ! Is it because I use FireFox/Ubuntu? Anyway, in my blogspot blog it got pasted properly: http://anaivecoder.blogspot.com/2010/04/binary-search-script.html :)

  550. Well, I thought I had it, I really did. It seemed right until I hit an edge case, an array of 2 items with the first of the 2 equal to the search value.

    So I guess that puts me in the fail category as well. But here it is after I debugged (mentally walked through the code line by line to see where it was missing the value). Really, I should have thought of the edge case before I tested. :-/

    function bsearch($value, &$array)
    {
    	sort($array);
    	$tmp = $array;
    	$len = count($array);
    	$position = 0;
    	while ($len > 1)
    	{
    		$mid = floor($len/2);
    		if ($tmp[$mid] == $value)
    		{
    			return $position+$mid;
    		}
    		else if ($tmp[$mid] > $value)
    		{
    			$tmp = ($len > 2)?array_slice($tmp, 0, $mid-1):array($tmp[0]); // initially did not check for 2 value array and would fail if $tmp[0] == $value
    			$len = count($tmp);
    			//position does not change
    		}
    		else // $array[$mid] is < $value
    		{
    			$tmp = array_slice($tmp, $mid+1);
    			$len = count($tmp);
    			// position shifts right
    			$position += $mid+1;
    		}
    	}
    	// still not found, and array length is now 1 item
    	if ($tmp[0] == $value) return $position;
    	else return -1;
    }
    
  551. Here was my initial attempt in ruby:

    def binary_search(to_find, sorted_array)
    return false if sorted_array.empty?
    middle_idx = sorted_array.size / 2
    middle = sorted_array[middle_idx]
    return middle if middle == to_find
    return binary_search(to_find, sorted_array[0..middle_idx]) if to_find middle
    end

    For the keen eyed, this does contain a mistake that I discovered in my quick testing attempts.

    My subsequent, corrected version is as follows:

    def binary_search(to_find, sorted_array)
    return false if sorted_array.empty?
    middle_idx = sorted_array.size / 2
    middle = sorted_array[middle_idx]
    return middle if middle == to_find
    return binary_search(to_find, sorted_array[0…middle_idx]) if to_find middle
    end

    Points for those who spot the differences ;) (There are two :) )

  552. Mike,

    You wrote: “JEIhrig ,please check once more the instructions at the bottom of the post on how to post code in a WordPress comment. If you don’t do this, then . Repost your comment using the correct form, and I will delete the old broken version.”

    I already read that but must have missed the instruction to replace the curly brackets with square ones. It wasn’t just the ‘greater than’ symbol that was missing but commented code somehow became uncommented, and another section was altogether missing, which lead me to believe it wasn’t mis-interpreted by wordpress but actually changed by someone. If it posts correctly this time, hopefully all my previous posts will be deleted.

    //I have not attempted to compile this
    int search(int *list, int high, int low, int key)
    {
    	int mid;
    	mid = (low + high) / 2;
    	if (high == low)
    	{
    		return key;
    	}
    	else if (key < list[mid])
    	{
    		return search(list, low, mid, key);
    	}
    	//The next line is commented like that intentionally. Please do not uncomment.
    	else //if (key > list[mid])
    	{
    		return search(list, mid + 1, high, key);
    	}
    }
    

    After re-pasting my code for the third time, (Still not compiling) I noticed a problem. It returns ‘key’ whether or not it’s in the list. So I changed that. (Does it still count if I made a modification after initial submission though I never compiled?)

    //I have not attempted to compile this
    int search(int *list, int high, int low, int key)
    {
    	int mid;
    	mid = (low + high) / 2;
    	if (high == low)
    	{
    		//Return true if it's in the list
    		return key == list[mid];
    		//Or this if you want the index
    		//return mid;
    	}
    	else if (key < list[mid])
    	{
    		return search(list, low, mid, key);
    	}
    	//The next line is commented like that intentionally. Please do not uncomment.
    	else //if (key > list[mid])
    	{
    		return search(list, mid + 1, high, key);
    	}
    }
    
  553. JEIhrig, I have deleted your previous posts now that this one is up, and properly formatted. Yes, I think it’s fine to change your submission so long as you make your change by inspection rather than only realising after tests have shown you the problem. Let us know how the tests go!

  554. I tried it. But I couldn’t help testing — not confident enough in my code to do that! And sure enough I got it wrong. Several times. Having test cases help.

    And, it’s one thing to have test cases. It’s another to have test cases that a) cover all cases, and b) are themselves correct!

    Looking forward to reading your summary :)

    Since I didn’t obey the rules, my submission doesn’t count… but posted anyways.

    let bsearch arr value =
    	let rec loop x y =
    		let range_length = y - x in
    		let midpoint = if range_length land 1 = 1
    			then range_length / 2 + 1 else range_length / 2
    		in
    		let mid = x + midpoint in
    		if range_length <= 0 then false
    		else if range_length = 1 then arr.(x) = value
    		else if arr.(mid) = value then true
    		else if arr.(mid) < value then
    			(* search right hand side *)
    			loop (mid+1) y
    		else
    			(* search left hand side *)
    			loop x mid
    	in loop 0 (Array.length arr)
    
  555. You know, not being allowed to test while writing code really screws with my TDD brain. Anyway, here’s a proposed solution. Yes, it’s iterative.

    Note that this code is in fact also in the never-run class. It checks out as syntactically correct, but I am braced to be horribly embarrassed by what happens once I run it.

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use POSIX qw(floor);
    
    sub binsearch {
        my ($what, @ary) = @_;
        # returns the index in sorted @ary that has $what
    
        my ($start, $end) = (0, $#ary);
        return unless defined $end; # @ary is empty
    
        while ($start <= $end) {
            my $middle = $start + floor(($end - $start) / 2);
            if ($ary[$middle] < $what) {
                $start = $middle + 1;
            } elsif ($ary[$middle] > $what) {
                $end = $middle - 1;
            } else {
                return $middle;
            }
        }
    
        # not found
        return;
    }
    
  556. Huzzah. It passes the test suite. If there’s a bug, it’s subtle enough that I didn’t think to test for it.

    D’A

  557. Not tested, written in 10-15 mins :

    (pseudo-code)

    
    int bsearch(value, array) {
    
    	min = 0;
    	max = lenght(array) - 1;
    
    	while(max > min) {
    		int mid = min + max / 2;
    		if (value <= array(mid)) {
    			max = mid;
    		} else {
    			min = mid + 1;
    		}
    	}
    
    	//Now min = max
    	if (array[min] == value) {
    		return min;
    	} else {
    		return -1;
    	}
    
    }
    

    After reading what other did in the comments, I realized that I wrote :
    int mid = min + max / 2;

    Instead of :
    int mid = min + (max-min) / 2;

    So I guess I failed the test…

  558. Luke Stebbing

    Not tested:

    from __future__ import division
    
    def bsearch(x, xs):
        """
        Locate the index of a value in a sorted list.
    
        Returns an index such that xs will still be sorted after calling xs.insert(index, x).
    
        If xs is not sorted, the behavior is undefined.
    
        """
        def recursive_bsearch(lower, upper):
            """Recursively search a closed-open range."""
            middle = int((lower + upper) / 2)
            # Don't evaluate xs[middle] if it falls outside the range.
            if middle == upper or x == xs[middle]:
                return middle
            # Shrink the closed-open range, being sure to exclude middle.
            if x < xs[middle]:
                upper = middle
            else:
                lower = middle + 1
            return recursive_bsearch(lower, upper)
        return recursive_bsearch(0, len(xs))
    
  559. Pingback: Top Posts — WordPress.com

  560. Eric Burnett: “@acastle: In your recurse I think it should be RETURN bsearch(…, but other than that your corrected version looks ok.”

    LOL, and that’s all it takes to fall into the 90%. One missed keyword and my function always returns -1, unless the item happens to be in the middle of the array on the initial call.

    Funny how reading through it, my mind interprets that function call as implicitly returning.

  561. I went with a simple recursion in Java. I found some bumps along the road while testing, but haven’t found the need to alter the search at all after I finished and decided to start testing.

    public Integer bsearch(int[] array, int start, int finish, int target) {
    		if (start > finish) {
    			return null;
    		}
    
    		int middle = (start + finish)/2;
    
    		if (array[middle] == target) {
    			return middle;
    		} else if (array[middle] > target) {
    			return bsearch(array, start, middle-1, target);
    		} else {
    			return bsearch(array, middle+1, finish, target);
    		}
    	}
    

    ps: actually, I made mistakes when testing, like forgeting I had a null return and printing out the array with the result as index, which obviously got a null pointer exception. Nothing on the bsearch itself, would like to see if I had any obvious mistake I still can’t see.

  562. Pingback: Binary search redux (part 1) « The Reinvigorated Programmer

  563. @JEIhrig: If key == list[mid], your code hits the else case and recurses with “return search(list, mid + 1, high, key);”, skipping that element and eventually returning false.

    @Greg: Mike said to ignore integer overflow in this, so you don’t have to worry about that case. However, it doesn’t look like you handled the case of array being empty, however :(.

    @Luke Stebbing: You do not handle the case where x is not in xs, nor when xs is empty. Essentially, there is no path for your code to return -1/False/None.

  564. The second edition doesn’t change this passage.

  565. seems to work, finds all the names in the list, and returns nil if the name wasn’t there

    def bsearch( list, item )
       return bsearchrec( list, 0, list.size()-1, item)
    end
    
    def bsearchrec( list, first,last, item )
       mid = (first+last)/2
       
       if (list.at(mid) == item)
          return item
       elsif first == last
          return nil
       else
          if( item < list.at(mid) )
             return bsearchrec(list, first, mid-1, item) 
          else
             return bsearchrec(list, mid+1, last, item)
          end
       end
    end
    
  566. Seems like my last comment didn’t go through. Anyway I have to say this is a little hard to believe. Where do they find those 90% of “professional” programmers? BSearch is seriously not that difficult to write. Anyway here’s my untested version in python:

    # Returns the index of data in inArray, or -1 if none found
    def BinarySearch(inArray, data):
        start = 0
        end = len(inArray) - 1
        if end < start:
            return -1
        if data < inArray[0] or data > inArray[-1]:
            return -1
        while (True):
            if (start == end):
                if data == inArray[start]:
                    return start
                else:
                    return -1
            middle = start + ((end-start) / 2)
            if (data == inArray[middle]):
                return middle
            elif (data < inArray[middle]):
                end = middle
            else:
                start = middle + 1
    
  567. Here are my test code btw:

    def Test(inArray, data):
        ret = BinarySearch(inArray, data)
        print "BinarySearch(",
        print inArray,
        print ", ",
        print data,
        print ") = ",
        print ret
    
    print
    Test([], 4)
    print
    Test([1], -1)
    Test([1], 1)
    Test([1], 2)
    print
    Test([1, 3, 5], 0)
    Test([1, 3, 5], 1)
    Test([1, 3, 5], 2)
    Test([1, 3, 5], 3)
    Test([1, 3, 5], 4)
    Test([1, 3, 5], 5)
    Test([1, 3, 5], 6)
    
  568. Success, took about 10 minutes. The Haskell code:

    import Data.Array
    
    mb :: Int -> Int -> Maybe (Int, Int)
    mb n m | n <= m    = Just (n, m)
           | otherwise = Nothing
    
    chop :: (Int, Int) -> (Maybe (Int, Int), Int, Maybe (Int, Int))
    chop (n, m) = (mb n (x-1), x, mb (x+1) m)
     where
        x = (m + n) `div` 2
    
    search :: Ord a => Array Int a -> a -> Bool
    search arr x = go (uncurry mb (bounds arr))
     where
        go Nothing   = False
        go (Just ys) = case chop ys of
                         (l, i, r) -> case compare x (arr!i) of
                                        LT -> go l
                                        EQ -> True
                                        GT -> go r
    
  569. This code passed all my tests the first time, so I’ll tentatively claim success. Though if I’m proven wrong, that’ll mean both my code AND my tests were crap…

    int binsearch(int *arr, int size, int target) {
    	int i, min = 0, max = size - 1;
    
    	while (min >= 0 && max < size && min <= max) {
    		i = ((max - min) / 2) + min;
    		
    		if (arr[i] > target)
    			max = i  - 1;
    		else if (arr[i] < target)
    			min = i + 1;
    		else
    			return i;
    	}
    
    	return -1;
    }
    
  570. Worked first time, took 7 minutes to code in Java including unit testing.

    I would not expect any developer to create a correctly working implementation first time, as there are too many ways to make easy mistakes – however, I would expect a good developer to be able to write, test, and complete a solid implementation in less than 30 minutes.

  571. I’m relatively new to programming and this was a fun challenge. Reading up it looks like my PHP solution is pretty close to Fred’s.

    In retrospect the code feels a bit too easy to trip up though – reading the comments I can see there’s quite a few cases I didn’t account for.

    <?php
    /*
    Qualifictions
    --Not accounting for possibility that needle isn't in haystack. 
    --No infinite loop protection if my code breaks.
    
    Assumes an array is passed in indexed with numeric keys beginning with 0.
    
    */
    function binary_search($haystack, $needle)
    {	
    	$lowLimit	= 0;
    	$highLimit	= count($haystack) - 1;	
    	$found 		= false;
    	
    	while ($found == false)
    	{
    		$range = $highLimit - $lowLimit; 
    		
    		if ($range%2 != 0)
    		{
    			if ($haystack[$highLimit] == $needle) {
    				$found = $highLimit;
    			}
    			$highLimit--; 
    			$range--;							
    		}
    		
    		$focus = ($lowLimit + ($range/2));
    		
    		switch ($haystack[$focus])
    		{
    			case ($haystack[$focus] == $needle):
    				$found = $focus;
    				break;
    			case ($haystack[$focus] > $needle): 
    				$highLimit = $focus - 1;
    				break;
    			case ($haystack[$focus] < $needle):	
    				$lowLimit = $focus + 1;
    				break;
    		}
    	}
    return $found;	
    }	
    
  572. In sporadic testing of my previous entry over the last 24 hours, I haven’t found a single case that fails, including the tests kindly provided by Steve Witham. While this isn’t completely conclusive, it’s a good sign that I’m in the 10%.

    A few notes:

    Writing the code took about 6 minutes. I spent another 5 scrutinizing the code to make sure I didn’t miss anything. I did try to import the code into the shell, which uncovered a syntax error that I fixed before my initial post (this was allowed by the rules).

    My solution didn’t include the workaround for overflow problems, but I’m not sure Python is susceptible to that particular issue. I know I’ve used the workaround in previous incarnations in the distant past; I don’t think I’ve worried about it since 16-bit days. It’s quite unlikely that I’ll feed it an array of greater than 2^30 in size, and the rules explicitly state that overflow can be ignored.

    I’m also quite surprised by the submissions that used slicing to divide the input array, because it defeats the whole purpose of a binary search. Binary search is supposed to be a O(log n) algorithm, but slicing is an O(n) operation.

  573. I can’t believe how few posters did this in Perl. My first pass was recursive (and worked) but for kicks I converted it to iterative (and forgot to save the old version).

    sub bs {
      $needle = shift;
      while (@_) {
        my $i = int(@_/2);
        my $cmp = $needle <=> $_[$i];
        if ($cmp == 0) {
          return 1;
        } elsif ($cmp == -1) {
          splice(@_,$i);
        } elsif ($cmp == 1) {
          splice(@_,0,$i+1);
        }
      }
      return 0;
    }
    
  574. Luke Stebbing

    @Eric Burnett: As I mention in bsearch()’s docstring, I’m interpreting the problem as “return the index where insertion preserves sorting”. IIRC, the original post didn’t state how edge cases should be reported, and I find that returning a valid index in all cases is much more useful than returning -1 or None. (I picked that up from the C++ STL convention of returning v.end() on a failure to match.)

    Given an empty list, my code returns 0, indicating that [].insert(0, value) preserves sorting: [] is sorted, as is [value]. Given bsearch(3, [1, 2, 4]), by inspection I believe I properly return 2, which is the position where 3 should be inserted to maintain sorted order. (I don’t have a development environment set up on my iPad yet, so I can’t verify that. That’s also the reason I didn’t test my solution.)

  575. After further analysis, I fail. Specifically, I will fail to find array(2) if array has size 6. Amusingly, I found the flaw by analyzing the code, and not by testing!

  576. @Luke Stebbing: Hmm, fair point. I should have read your doc string more carefully – I retract my comments :).

  577. shit, didn’t read the quoting instructions fully:

    def bsr(target, list)
      len = list.length
      mid = len/2;
    
      if(len == 0)
        return false;
      elsif(len == 1)
        return list[0] == target;
      elsif(target < list[mid])
        return bsr(target, list[0,mid]);
      else
        return bsr(target, list[mid+1,len-1]);
      end
    end
    
  578. Tomas Henriquez

    def bs(array,findme):
    if(array):
    array = sorted(array)
    len = array.__len__()

    pos=len/2
    mid=len/2+1

    while(mid):
    #trunk because its an integer
    if mid%2 and mid != 1: mid=mid/2+1
    else: mid=mid/2
    if (findme == array[pos]):
    return array[pos]
    elif (findme > array[pos]):
    pos+=mid
    elif (findme len-1 or pos < 0): break

    return "None"

    a = [1,5,3,4,7,8,9]
    print bs(a,9)
    a = [1,5,4,3,9]
    print bs(a,-12)
    print bs(a,3)
    a = [-19,-3,34,5,9,12]
    print bs(a,-32)
    print bs(a,34)
    print bs(a,324)
    a = [-21,-3,14,42,54,2,43,1,5,7,2,-14,423,-3]
    print bs(a,-21)
    print bs(a,423)
    print bs(a,12424)
    print bs(a,7)

  579. Tomas Henriquez

    oh fack… i hate wordpress, well deduce the tabs hehe

  580. Success. Or, erm, at least it passes the tests I tried.

    Code:

            public static int BinarySearch<T>(T[] array, T item)
                where T : IComparable<T>
            {
                if (array == null) throw new ArgumentNullException("array");
                int startIndex = 0, endIndex = array.Length;
                while (startIndex < endIndex)
                {
                    int middleIndex = startIndex + (endIndex - startIndex) / 2;
                    T middleItem = array[middleIndex];
                    int comparison = item.CompareTo(middleItem);
                    if (comparison > 0)
                        startIndex = middleIndex + 1;
                    else if (comparison < 0)
                        endIndex = middleIndex;
                    else
                        return middleIndex;
                }
                return -1;
            }

    Tests:

            [TestMethod, ExpectedException(typeof(ArgumentNullException))]
            public void ArgumentNullException_Is_Thrown() { BinarySearch(null, 0); }
            [TestMethod]
            public void Odd_Number_Of_Items_Middle() { Assert.AreEqual(2, BinarySearch(new int[] { 1, 2, 3, 4, 5 }, 3)); }
            [TestMethod]
            public void Even_Number_Of_Items_Middle_Minus_One() { Assert.AreEqual(2, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 3)); }
            [TestMethod]
            public void Even_Number_Of_Items_Middle_Plus_One() { Assert.AreEqual(3, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 4)); }
            [TestMethod]
            public void Even_First_Item() { Assert.AreEqual(0, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 1)); }
            [TestMethod]
            public void Even_Last_Item() { Assert.AreEqual(5, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 6)); }
            [TestMethod]
            public void Odd_First_Item() { Assert.AreEqual(0, BinarySearch(new int[] { 1, 2, 3, 4, 5 }, 1)); }
            [TestMethod]
            public void Last_Item() { Assert.AreEqual(4, BinarySearch(new int[] { 1, 2, 3, 4, 5 }, 5)); }
            [TestMethod]
            public void Nonexistant_Item_In_Middle() { Assert.AreEqual(-1, BinarySearch(new int[] { 1, 2, 3, 5, 6 }, 4)); }
            [TestMethod]
            public void Nonexistant_Item_Past_End() { Assert.AreEqual(-1, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 7)); }
            [TestMethod]
            public void Nonexistant_Item_Before_Beginning() { Assert.AreEqual(-1, BinarySearch(new int[] { 1, 2, 3, 4, 5, 6 }, 0)); }
            [TestMethod]
            public void Empty_List() { Assert.AreEqual(-1, BinarySearch(new int[] { }, 7)); }
  581. Peter Toneby

    My solution found the correct value as far as I could determine with my test cases, but only returned a boolean. When I switched to use Steve Whithams test cases they expected a return value of position and steps I failed on the position (after converting my code to return those, of course). Not sure if that should count as a fail or sucess. Thanks to Steve for the test cases.

    Here is the current code:

    def binary_search(arr, value, index=0, nsteps=0):
        """\
        Return i, nsteps.
        i is any int such that A[i] == T, else i = None
        nsteps is the number of steps it took.
        """
        if arr == None or arr == []:
            return None, nsteps
        if len(arr) == 1:
            if arr[0] == value:
                return index, nsteps
            return None, nsteps
        split = len(arr)/2
        if arr[split] == value:
            return index+split, nsteps
        elif arr[split] > value:
            return binary_search(arr[:split], value, index, nsteps+1)
        elif arr[split] < value:
            index += split + 1
            return binary_search(arr[split+1:], value, index, nsteps+1)
    
  582. C, 15 minutes.

    /** begin points to the start of the array,
    *   end is (begin+length). Returns a pointer
    *   to the answer, or NULL if not found. */
    int* bs(int* begin, int* end, int needle)
    {
      while(begin < end)
      {
        int* pivot = begin + (end-begin)/2;
    
        if(needle < *pivot)
            end = pivot;
        else if(*pivot < needle)
            begin = pivot+1;
        else
            return pivot;
      }
      return NULL;
    }
    
  583. Luke Stebbing is right — I didn’t specify what the search should return when the element is not in the array. That was careless of me (although I suppose I could pass the buck to Jon Bentley :-)). Luke, I did mean for an out-of-band value such as -1 to be returned, as most (I think all) of the other solutions have done; but your approach is not prohibited by my statement of the problem, so you get a pass. (Provided there are no other bugs, of course!)

    By the way, I love that Eric is continuing to fight the good fight. Keep up the destructive work!

  584. Got it correct (I guess) after 10 iteration or so right here in Firebug.
    {source}
    function bs(arr, num) {
    lower = 0;
    upper = arr.length – 1;
    while (upper != lower) {
    mid = Math.floor((upper + lower)/2);
    if (num == arr[mid]) return mid;
    if (mid == lower) return num == arr[upper] ? upper : null;
    if (num arr[mid]) lower = mid;
    }
    return null;
    }
    {/source}

  585. {source}
    private static int find(int t, int[] list) {
    if (list.length == 0) {
    return -1;
    }
    return find(t, list, 0, list.length-1);

    }

    private static int find(Integer t, int[] list, int lower, int upper) {
    if (upper < lower) {
    return -1;
    }
    int probe = (upper+lower)/2;
    switch (t.compareTo(list[probe])) {
    case 0:
    return probe;
    case -1:
    return find(t, list, lower, probe-1);
    case 1:
    return find(t, list, probe+1, upper);
    }
    return -1;
    }
    {/source}

    (Java) no idea if it works yet, I'm dimly aware that "upper+lower" might overflow for a truly gigantic array, but decided not to worry about that.

  586. 10 minutes, failed at testing for using

    <

    instead of

    <=

    :(

    def bsearch(li, val, lo=None, up=None):
        if lo is None or up is None:
            lo = 0
            up = len(li)
    
            # check that the value is inside the list
            if val < li[lo]:
                return None
            if val > li[up - 1]:
                return None
        print li, val, lo, up
    
        # the interval to check is [lo, up), check its size
        if (up - lo) <= 1:
            if li[lo] == val:
                return lo
            else:
                return None
    
        # slice and dice!
        mid = (lo + up) / 2
    
        if (li[mid] <= val):
            return bsearch(li, val, mid, up)
        else:
            return bsearch(li, val, lo, mid)
    
  587. 
    public static int binarySearch(int[] searchArray, int lowIndex, int highIndex, int searchKey) {
            int index = -1;
    
            if (lowIndex >= highIndex) {
                index = -1;
            } else {
    
                int midIndex = (lowIndex + highIndex) / 2;
                int midKey = searchArray[midIndex];
    
                if (searchKey == midKey) {
                    index = midIndex;
                } else if (searchKey < midKey) {
                    index = binarySearch(searchArray, lowIndex, (midIndex - 1), searchKey);
                } else if (searchKey > midKey) {
                    index = binarySearch(searchArray, (midIndex + 1), highIndex, searchKey);
                }
            }
    
            return index;
        }
    
    
  588. Groovy version:

    def binarySearch(List tab, Number query){
    if (tab.size() == 0) return null;

    def s = 0, e = tab.size() -1
    while (s<e){
    int m = s+e/2
    if (query <= tab[m]) e = m else s = m+1
    }
    if (tab[s] != query) return null;
    return s;
    }

  589. Luke Stebbing

    @Mike Taylor: What I like about returning a valid index in all cases is it allows you to avoid a conditional expression when you don’t need one, i.e. if you’re using bsearch() to build a sorted list. The possibility of a wild and crazy index like -1 forces you to guard every single call in all situations.

    I’ve firmly believed the “Make sure special cases are truly special” mantra ever since I encountered overly specific definitions in ninth-grade geometry, and I think I’m following that rule here by treating a failure to find the element normally.

    Here’s an O(1) way to determine whether my bsearch() found the value. I really should’ve returned (index, found) in the first place, since that would’ve been a more useful signature. (I also should’ve used Python’s a // b shorthand instead of int(a / b), but I simply forgot.)

    index = bsearch(x, xs)
    found = index != len(xs) and xs[index] == x
    
  590. Failed without testing……. yeah I forgot arrays dont always have to be an ‘even’ length ;). Once wrote a few tests, found ‘bug’ within minutes (seconds tbh).

  591. Sorry, my first post seems to be screwed up. So I post again. I did not test the code before hand, and I did not follow the google blog link before writing this.

    int BinarySearch(vector<int> arr, int findThis)
    {
    	if (arr.size() <= 0)
    		return -1;
    
    	int minIndex = 0;
    	int maxIndex = arr.size() - 1;
    	
    	while (true)
    	{
    		int poolIndex = minIndex + ((maxIndex - minIndex) >> 1);
    
    		if ((minIndex >= arr.size() ||
    			 maxIndex < 0 ||
    			 maxIndex == minIndex) &&
    			arr[poolIndex] != findThis)
    			return -1;
    
    		if (arr[poolIndex] == findThis)
    			return poolIndex;
    		else if (arr[poolIndex] > findThis)
    			maxIndex = poolIndex - 1;
    		else if (arr[poolIndex] < findThis)
    			minIndex = poolIndex + 1;
    	}
    
    	return -1;
    }
    
  592. PHP, $range is array, $so is element to find

    function binSearch($range, $so) {
    $elems = count($range);
    if ($elems == 0) return false;
    if ($elems == 1) if ($range[0] == $so) return true; else return false;
    $middle = $elems % 2 == 0 ? (($range[($elems/2)-1] + $range[($elems/2)]) / 2) : $range[floor($elems / 2)];
    if ($middle == $so) return true;
    $range = ($middle < $so) ? array_slice($range, ceil($elems/2)) : array_slice($range, 0, floor($elems/2));
    return binSearch($range, $so);
    }

  593. — | maybe find a value a in a list of a. took about 20 minutes to be honest.
    — admittedly i wrote it so it searched descending order lists 5,4,3,2,1 and realised
    — this probably wasn’t desired, so changed “GT” to “LT”
    find :: (Ord a) => a -> [a] -> Maybe a
    find k [] = Nothing
    find k xs = go (length xs) xs where
    go len [x] | compare k x == EQ = Just x
    | otherwise = Nothing
    go len xs = go len’ xs’ where
    xs’ = case last up `compare` k of
    LT -> down
    otherwise -> up
    — i dunno if round 2.5 == 3
    len’ | even len = len `div` 2
    | otherwise = (len `div` 2) + 1
    (up,down) = (take len’ xs,drop len’ xs)

    seems to work alright

  594. def binary_search(a,s, l=0, u=nil)
    u = a.length-1 if u==nil
    x = l+(u-l)/2
    return x if s==a[x]
    return “not found” if (u-l)a[x])
    return binary_search(a,s,l,x) if (s<a[x])
    end

  595. Simple algorithm, takes an hour, but failed 3 stupid mistakes. Blame on me.

    int bsearch1(int what, int elements, int* ar){
    int max_el = elements – 1;
    int min_el = 0;
    //
    for (;(min_el=0)&&(max_el<elements);){
    int ix;
    int delta;
    //
    if ( 0 != (max_el % 2)){
    if (ar[max_el] == what)
    return max_el;
    — max_el;
    };
    if ( 0 != (min_el % 2)){
    if (ar[min_el] == what)
    return min_el;
    ++ min_el;
    };
    //
    delta = (max_el – min_el)/ 2;
    ix = min_el + delta;
    //
    if (ar[ix] what)
    max_el = ix – 1;
    else
    return ix;
    //
    };
    //
    return -1;
    };

  596. It took me like five minutes, no errors at the first try:

    {source}
    def bsearch(ary, val):
    lo = 0
    hi = len(ary)
    oldm = -1
    while True:
    m = int((lo+hi)/2)
    if oldm == m:
    return -1
    if val ary[m]:
    oldm = m
    lo = m
    else:
    return m
    {/source}

    My guess is that most programmers (even experienced ones) do not mentally check for the obvious edge cases while they’re coding.

  597. This has passed all the tests I’ve thrown at it since writing it. (Python)

    def binary_search(array, value):
        a = 0
        b = len(array) - 1
        while a <= b:
            m = (b - a) / 2 + a
            if array[m] == value:
                return m
            elif array[m] > value:
                b = m - 1
            else:
                a = m + 1
        return -1
    
  598. Yay! I’m one of the best 10%! Below is my Java-source, I’ve made it generic instead of just ints.

    	/**
    	 * Perform a binary search on a pre-sorted list.
    	 * 
    	 * @param <T>					The type of the searchedValue, must implement {@link Comparable}
    	 * @param listToBeSearchedIn	The list to search in
    	 * @param searchedValue			The value to search
    	 * @param start					The start index, inclusive
    	 * @param end					The end index, exclusive
    	 * @return						The index of searchedValue in listToBeSearchedIn, or -1 if not present
    	 */
    	public static <T extends Comparable<T>> int binarySearch(List<T> listToBeSearchedIn,
    														     T searchedValue,
    														     int start,
    														     int end) {
    		int searchSize = end - start;
    		if(searchSize == 0) { //It is not in the list
    			return -1;
    		} else { //Split up the remaining search area and continue in one of the parts
    			int middle = start + (searchSize/2); //Division possibly truncated
    			int difference = searchedValue.compareTo(listToBeSearchedIn.get(middle));
    			if(difference == 0) { //We found it!
    				return middle;
    			} else if(difference < 0) { //Go left
    				return BinarySearch.binarySearch(listToBeSearchedIn, searchedValue, start, middle);
    			} else { //Go right
    				return BinarySearch.binarySearch(listToBeSearchedIn, searchedValue, middle+1, end);
    			}
    		}
    	}
    
  599. Argh, layout is all fucked up. I guess I shouldn’t have used tabs… Anyway, clicking on ‘View Source’ helps a little…

  600. @Mike: Thanks :). I should point out that I’m not checking 100% of them though… mis-formatted solutions I think code is missing from are skipped, as are languages I can’t wrap my head around yet (*cough* Haskell *cough*).

    @Daniel: Just a nitpick – list[mid+1, len-1] is asking for len-1 elements starting at mid+1. Because there aren’t len-1 elements left you’ll get the ones to the end of the array (as desired), but it would be clearer to just say list[mid+1..len-1].

    Also, as noted by other people, using array slices makes the algorithm O(n) rather than O(logn).

    @Peter Toneby: What exactly you return is not specified, so not exactly matching someone else’s test cases is fine. I think if it passed in original form, that is enough. Your updated code looks good, although I will note that using array slices will make your search be O(n) not O(logn) as well.

    @mondodello: It appears your code will mis-handle single element arrays (or ones that end up single element after recursion) since in that case, lowIndex == highIndex. If the first “if” is changed from greater-than-or-equal to just greater-than, I think your code would work.

    @Piotr Gabryanczyk: “int m = s+e/2”: I think operator precedence will get you on this one.

  601. failed to read instructions, tested before submitting, the only bug that uncovered was a ” which I’m sure I would have found by analysis.

    {source}
    int binarysearch(int *data, int sizeOfData, int lookFor)
    {
    int chopsize = sizeOfData / 2;
    int index = chopsize;

    // loop until the split size is 0 or value is found.
    while( data[index] != lookFor && chopsize )
    {
    chopsize /= 2;

    if (data[index] > lookFor)
    index -= chopsize;
    else
    index += chopsize;
    }

    return (data[index]==lookFor)?index:-1;
    }

    {source}

  602. int bsearch(int *L, int len, int T){
        int i;
        if len = 0
            return -1;
        i=len/2;
        if L[i]=T
            return i;
        if L[i] < T
            return bsearch(L+i+1, len-i-1, T);
        if L[i] > T
            return bsearch(L, i, T);
    }
    

    Failed miserably

  603. Paul Annesley


    #!/usr/bin/env ruby
    #
    # Are you one of the 10% of programmers who can write a binary search?
    # https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    def bin_search(search, subject)
    discarded_left = 0
    while subject.any?
    mid = subject.length / 2
    comparison = search <=> subject[mid]
    return comparison.zero? ? discarded_left : nil if subject.one?
    case comparison
    when1
    subject = subject[0mid]
    when 1
    subject = subject[midsubject.length]
    discarded_left += mid
    else
    return discarded_left + mid
    end
    end
    end
    ###
    # Tests
    $passes = 0
    $fails = 0
    def assert_bin_search(expect, search, subject)
    found = bin_search(search, subject);
    pass = found === expect
    puts "FAIL: search:%d expect:%s got:%s in [%s]" %
    [search, expect.inspect, found.inspect, subject.join(',')] unless pass
    pass ? $passes += 1 : $fails += 1
    end
    subject = [4, 5, 7, 11, 19, 29, 42, 69, 129, 182, 189, 190, 250]
    # test each value
    subject.each_with_index do |value, index|
    assert_bin_search(index, value, subject)
    end
    # test out-of-bounds and gaps
    ((0..500).to_asubject).each do |value|
    assert_bin_search(nil, value, subject)
    end
    # test empty subject
    assert_bin_search(nil, 1, [])
    puts "Passes: #{$passes} Fails: #{$fails}"

    view raw

    binsearch.rb

    hosted with ❤ by GitHub


    paulbookpro:binsearch paul$ ./binsearch.rb
    Passes: 502 Fails: 0

    view raw

    output.txt

    hosted with ❤ by GitHub

    I broke rule #6; I guess test driven development has made me soft. My mind hasn’t been trained outside an iterative development model. And since we’ve evolved past punch-cards, I’m okay with that :)

  604. I spent 17 minutes for thinking. After that I spent 21 minutes for writing test fixtures (that I will run only after all code finished).
    After that I spent 9 minutes on binary search function.
    At all it takes for me 47 minutes.
    After that I run all tests and all works fine with first run!
    After all I readed comments and links wih typical problems – all of them was solved by me during thinking.

    there is my code:
    http://pastie.org/928040

  605. Dmitry, you should win some kind of prize: you belt-and-braces solution of both thinking and testing is clearly the way to go. I an encouraged that, after you’d invested the time into design, it worked first as intended the first time.

    Now we wait for Eric to break it :-)

  606. #!/usr/bin/perl
    
    sub bs {
      my ($pos, $x, @ar) = @_;
    
      return 'not found' unless scalar(@ar);
      
      if ($#ar == 0) {
        if ($x == $ar[0]) {
          return $pos;
        } else {
          return 'not found';
        }
      }
    
      if ($x > $ar[int($#ar/2)]) {
        return bs($pos + int($#ar/2)+1, $x, @ar[int($#ar/2)+1..$#ar]);
      } else {
        return bs($pos, $x, @ar[0 .. int($#ar/2)]);
      }
    }
    
    my @test = (1,3,5,7,9,11,13,15,45,45,45,45,45,56,56,56,56,57);
    
    print bs(0, 57, @test);
    print "\n";
    

    Seems to work.

  607. I wrote this in five minutes. I didn’t read the rules and ran a single test that found a bug in my stop condition, (= lower (- upper 1)). I fixed that condition, and it passed all my tests.

    My algorithm would fail in a language that doesn’t handle bignums transparently, because I did not take into account the overflow issue. I had forgotten that problem altogether! Yet another good reason to re-read Bentley occasionally.

    (define search-in-range
      (lambda (key v lower upper)
        (if (= lower (- upper 1))
            (if (= key (vector-ref v lower))
                lower
                -1)
            (let ((middle (floor (/ (+ lower upper) 2))))
              (if (>= key (vector-ref v middle))
                  (search-in-range key v middle upper)
                  (search-in-range key v lower middle))))))
    
  608. @Will: Lets see…your code does not handle empty arrays (sizeOfData == 0), assumes the length is a power-of-two (otherwise successively dividing by 2 will not cover the array properly), and even when it is, cannot find items in element 0 (eg 4-2-1=1, so successively subtracting the new chopsize will not get to index 0 before chopsize reaches 0 itself). Sorry :(.

    @Dmitry/Mike: Looks good to me :).

    @Ramesh: In the second recurse, should it be “@ar[0 .. int($#ar/2)-1]” instead? I think the middle element is ending up copied down unnecessarily (caveat: I don’t know perl). Also, the standard warning that performance goes to O(n) when using list slices applies. That said, it looks like the code should work :).

  609. {source}
    int binary_search(int val,int *arr,size_t len)
    {
    int lo = 0 , hi = len -1;
    while(lo <= hi) {
    int mid;
    mid = (hi + lo)/2 ;
    if(arr[mid] == val)
    return val;
    if(arr[mid] val)
    hi = mid-1;
    else
    return -1;

    }
    return -1;
    }
    {/source}

  610. 
    /* C - MSVC 6 Implementation - Untested.   Fire Away*/ 
    
    #include <stdio.h>
    
    int binary_search      /* Return 1 when found, and 0 when not found */ 
    (
        int    *haystack,  /* Search this array                                   */ 
        size_t  start,     /* First subscript                                     */
        size_t  end,       /* Last subscript                                      */
        int     needle,    /* For this value                                      */ 
        size_t *subscript  /* Set this to the subscript where the value was found */ 
    )
    {
        if (haystack != NULL && subscript != NULL)
        {
            while (start < end)
            {
                size_t mid = (start + end) / 2;
    
                if (haystack[mid] < needle)
                {
                    start = mid + 1;
                }
                else
                {
                    if (haystack[mid] > needle)
                    {
                        end = mid - 1;
                    }
                    else
                    {
                        *subscript = mid;
    
                        return 1;
                    }
                }
            }
        }
    
        return 0;
    }
    
    
    int main(void)
    {
        return 0;
    }
    
    
  611. I wouldn’t consider myself a programmer (get out clause?), but I did have a go and I failed (with my first attempt) – basically because I was never going to get anywhere without testing. If interested, I’ve documented my attempts on my website. I think I pretty much got there in the end.

    I would like to say thanks for the challenge though. It was fun.

  612. @Eugene Wallingford: “(search-in-range key v middle upper)” should probably be “(search-in-range key v (+ middle 1) upper)” since there is no need to keep the middle element around, but the code looks good otherwise.

  613. Christopher Oliver

    I started a version in Common Lisp and FAILED! Not that I’m ignorant in Lisp though I’m far from an expert, but that I’ve dealt with this recently in Smalltalk, and misremembered stuff. Also I’ve been under fair stress with my heart dog being sick with colitis and no clear diagnosis or cure in view. I’ve not had this sort of anguish since my dad died of cancer. Stress plays havoc with concentration.

    One thing I note is that few is any of these answers deal with the question: “what comes before ‘elephant?'” Sometimes it’s useful to note the insertion index if you fail to find something. In languages like Scheme and Lisp, you’d
    use (values …) to return an index and whether it was a find or insertion point.

    int berserk(void *item,
    	    void *array[],
    	    int size,
    	    int (*compare)(void *, void *),
    	    int *where_found)
    {
      int low, high;
    
      low = 0;
      high = size-1;
    
      while (low <= high) {
        int midpoint;
        int comparison;
        
        midpoint = (low+high)/2;
        comparison = compare(item, array[midpoint]);
    
        if (comparison == 0) {
          *where_found = midpoint;
          return 0;
        }
    
        if (comparison > 0)
          low = midpoint + 1;
        else
          high = midpoint - 1;
      }
    
      *where_found = low;
      return -1;
    }
    
  614. I had 10 minutes to kill at work, so… here’s a C# version that uses recursion. It returns -1 if the item was not found. Couldn’t be bothered to make it generic, but the algorithm is the same of course.


    public static int BinarySearch(int[] data, int value)
    {
    return BinarySearch(data, value, 0, data.Length - 1);
    }

    public static int BinarySearch(int[] data, int value, int min, int max)
    {
    if (min > max)
    return -1;

    if (min == max)
    return data[min] == value ? min : -1;

    // If your array is larger than one billion elements: tough luck :)
    int pos = (min + max) / 2;

    if (value == data[pos])
    return pos;
    else if (value < data[pos])
    return BinarySearch(data, value, min, pos - 1);
    else
    return BinarySearch(data, value, pos + 1, max);
    }

    Now if the compiler or JIT’er only supported tail recursion on 32 bit systems as well as x64 :)

  615. Mike Beverley

    Even if my algorithm is right, i’m apparently too slow for simple instructions. Here’s a third try:

    static int? BinarySearch (int[] sortedArray, int T)
    		{
    			if (sortedArray == null) {
    				return null;
    			} else {
    				int location = -1;
    				double upperBound = sortedArray.Length - 1;
    				double lowerBound = 0;
    				int pivot;
    				int valueAtPivot;
    
    				while (upperBound >= lowerBound) {
    					if (upperBound != lowerBound) {
    						pivot = Convert.ToInt32 ((Math.Ceiling ((((upperBound - lowerBound) / 2) + lowerBound)) - 1));
    						valueAtPivot = sortedArray[pivot];
    						if (valueAtPivot != T) {
    							if (valueAtPivot > T) {
    								upperBound = pivot - 1;
    							} else {
    								lowerBound = pivot + 1;
    							}
    						} else {
    							location = pivot;
    							break;
    						}
    					} else {
    						if (sortedArray[Convert.ToInt32(upperBound)] == T) {
    							location = Convert.ToInt32(upperBound);
    						}
    						break;
    					}
    				}
    				if (location == -1) {
    					return null;
    				} else {
    					return location;
    				}
    			}
    		}
    
  616. Christopher Oliver

    Of course what I think this may point out is not that ninety percent of programmers are stupid, but that many live in circumstances that interfere with their best concentration.

    That being said, I wouldn’t write many lines of code for real without exercising them.

  617. I had 10 minutes to kill at work, so… here’s a C# version that uses recursion. It returns -1 if the item was not found. Couldn’t be bothered to make it generic, but the algorithm is the same of course.

    public static int BinarySearch(int[] data, int value)
    {
        return BinarySearch(data, value, 0, data.Length - 1);
    }
    
    public static int BinarySearch(int[] data, int value, int min, int max)
    {
        if (min > max)
            return -1;
    
        if (min == max)
            return data[min] == value ? min : -1;
    
        // If your array is larger than one billion elements: tough luck :)
        int pos = (min + max) / 2;
    
        if (value == data[pos])
            return pos;
        else if (value < data[pos])
            return BinarySearch(data, value, min, pos - 1);
        else
            return BinarySearch(data, value, pos + 1, max);
    }
    

    Now if the compiler or JIT’er only supported tail recursion on 32 bit systems as well as x64 :)

    I did run some basic tests later at home (just now). Seems fine apart from the 2^30 limit.

  618. Had to join the fun! After some thinking, writing, rethinking and rewriting, I managed to produce something that actually worked pretty well with a few test cases.

    And when running through Steve Witham’s test cases it seems to hold up as well…

    Thanks for an interesting post!

    def binsearch(ar, v, s=0, e=ar.length)
      return nil if s >= e
      return ar[s] == v ? s : nil if e-s == 1
      
      pivot = ((e+s) / 2)
      return ar[pivot] > v ? binsearch(ar, v, s, pivot) : binsearch(ar, v, pivot, e)
    end
    

    (Ugly Ruby code to run test cases on my site.)

  619. Knocked up an iterative solution in Groovy: 4 minutes, 4 lines. I’ve included the passing test below (1,000,000 random test cases).

    def bsearch(t, list) {
        if (list.size() == 1) return t == list[0]
        def mid = (list.size()+1).intdiv(2) - 1
        return t <= list[mid] ? bsearch(t, list[0..mid]) : bsearch(t, list[(mid+1)..-1])
    }
    
    def r = new Random()
    1000000.times {
        def list = (0..r.nextInt(100)).collect{ r.nextInt(50) }.sort()
        def t = r.nextInt(100)
        def expectedResult = list.contains(t)
        def result = bsearch(t, list)
        if (result != expectedResult) {
            println "OOPS! t: $t, list: $list\n>>> Expected $expectedResult, but got $result"
            assert false
        }
    }
    println "It works!"
    
  620. public class BinarySearch {
    
    
    	BinarySearch(){};
    		
    	public int search(List<Integer> list, int value) throws Exception{
    			
    		if(list == null || list.isEmpty())
    			throw new Exception("Cant Search a null or empty list");
    			
    		return search(list, value, 0, list.size()-1);
    	}
    		
    	private int search(List<Integer> list, int value, int begin, int end) throws Exception{
    			
    		if (begin == end) throw new Exception("Value " + value + " not found");
    			
    		int middle = (begin + end) / 2;
    			
    		if (list.get(middle) == value)
    			return middle;
    		else if (list.get(middle) > value)
    			return search(list, value, middle, end);
    		else return search(list, value, begin, middle);
    	}
    }
  621. Of course, I meant “recursive”, rather than “iterative”.

  622. Works for me:

    from math import ceil
    
    def bsearch(needle, haystack):
        top = len(haystack)-1
        bottom = 0
        
        # check that needle can still possibly exist in haystack
        if (not haystack) or haystack[-1] < needle or haystack[0] > needle:
            return None
        
        # get a value for middle val
        middle = (len(haystack) - (len(haystack)%2)) / 2
        middle_val = haystack[middle]
    
        # check value
        if needle == middle_val:
            return middle
        elif needle < middle_val:
            return bsearch(needle, haystack[:middle])
        else:
            return middle + 1 + bsearch(needle, haystack[middle+1:])
    
  623. UnoriginalGuy

    I got it on my second compile (I inverted end – start giving me negative numbers originally).

    I also added the overflow protection after the fact (because frankly who wants a bsearch that crashes if you fly outside the limits of the array?).

            static int bsearch(int start, int end, int[] haystack, int needle)
            {
                int middle =  (int)Math.Round( ( (double)(end - start) / 2)  )  + start;
                if (haystack[middle] == needle)
                    return middle;
                if (middle == haystack.Length-1 || middle == 0)
                    return -1; 
                if (needle < haystack[middle])
                    return bsearch(start, middle, haystack, needle);
                if (needle > haystack[middle])
                    return bsearch(middle, end, haystack, needle); 
                return -1; 
            }
  624. Not sure if I got it right the first time, because I made a bollix of the test’s logging function :(

    Second time round I think I got it (and the logging function!) with this:

    def binary_search(needle, haystack):
        start = 0
        length = len(haystack)
        end = length - 1
        idx = length / 2
        while True:
            val = haystack[idx]
            if val == needle:
                return idx
            elif val > needle:
                end = idx
            elif val < needle:
                start = idx
            idx = start + ((end - start) / 2)
    

    It was a lot easier than I expected it to be (only 10% of programmers!), so I’m assuming I got it wrong somewhere.

  625. I tested and fail.
    It was wrong the comparison (

    >

    instead of

    <

    ), didn´t find the last item
    and loop for ever when the value wasn´t found

    Here is the corrected code.

    public int search(List<Integer> list, int value) throws Exception{
    			
    		if(list == null || list.isEmpty())
    			throw new Exception("Cant Search a null or empty list");
    		particion = 0;
    		return search(list, value, 0, list.size());
    	}
    		
    	private int search(List<Integer> list, int value, int begin, int end) throws Exception{
    		particion++;
    		if (begin == end) throw new Exception("Value " + value + " not found");
    		
    		int middle = (begin + end ) / 2;
    			
    		if (list.get(middle) == value)
    			return middle;
    		else if (list.get(middle) < value)
    			return search(list, value, middle+1, end);
    		else return search(list, value, begin, middle);
    	}
    
  626. Phillip Howell

    Developed without testing and then tested against a few hand-picked edge cases and 2 million arrays of randomly generated size <= 50 with randomly generated values. This code does no special handling for arrays that contain ranges with the same value repeated; if the value is equivalent to the target it will pick whichever one it happens to land on first. (This behavior is different from that of std::find, but is correct w/r/t the Bentley description of the algorithm.)

    It's unclear to me why anyone would have trouble writing this algorithm; I guess some would forget to range-check the inputs?

    template <typename T>
    const T* binsearch(const T* start, const T* end, const T& target)
    {
        if (start == end || target < *start || target > *(end - 1))
        {
            return end;
        }
        
        size_t middle = (end - start) / 2;
        
        if (target == start[middle])
        {
            return start + middle;
        }
        else if (target > start[middle])
        {
            return binsearch(start + middle, end, target);
        }
        else // (target < start[middle])
        {
            return binsearch(start, start + middle, target);
        }
    }
    
  627. 
    /* 
        C - MSVC 6 Implementation - Untested.
        I am a 90%er.
    
        The code was developed on paper.  I 
        mistranscribed "<=" as "<" when submitting
        the code.
      
    */
    
    #include <stdio.h>
    
    int binary_search      /* Return 1 when found, */ 
    (                      /* and 0 when not found */ 
    
        int    *haystack,  /* Search this array    */ 
        size_t  start,     /* First subscript      */
        size_t  end,       /* Last subscript       */
        int     needle,    /* For this value       */ 
        size_t *subscript  /* Set this to where    */
                           /* the value was found  */ 
    )
    {
        if (haystack != NULL && subscript != NULL)
        {
            while (start <= end)
            {
                size_t mid = (start + end) / 2;
    
                if (haystack[mid] < needle)
                {
                    start = mid + 1;
                }
                else
                {
                    if (haystack[mid] > needle)
                    {
                        end = mid - 1;
                    }
                    else
                    {
                        *subscript = mid;
    
                        return 1;
                    }
                }
            }
        }
    
        return 0;
    }
    
    
    int main(void)
    {
        return 0;
    }
    
    
  628. Iterative Python version.
    http://pastie.org/928662

    I had almost exactly the same experience as Todd Moon. I started writing code, got something I felt tentatively OK about, then I went through a couple obvious corner cases on paper and found a bug. I fixed that bug, went through the rest of my corner cases, simplified a little, then declared myself done; *then* I wrote the embedded tests, ran them, and they all passed on the first go.

    All told , it took me about an hour. Feels like a long time to do such a ubiquitous algorithm in such a simple language!

    BTW, I slightly wish the challenge was a bit more clearly specified. Looking through the comments now, I see some people simply treated it as a boolean exercise (Is T in the array or not?), while others (like me) return the index where T is found, or some other value to signal failure. That’s fine, but it would’ve been really cool to go further and have each submission be eg. a command-line program that could be tested by the same test script.

  629. Oh, and Philip Howell pointed out another under-specified bit of the problem. Do we care about finding the lowest occurrence of T, or is it OK to stop wherever you happen to find it? I did the latter.

  630. Paul suggested:

    BTW, I slightly wish the challenge was a bit more clearly specified. Looking through the comments now, I see some people simply treated it as a boolean exercise (Is T in the array or not?), while others (like me) return the index where T is found, or some other value to signal failure. That’s fine, but it would’ve been really cool to go further and have each submission be eg. a command-line program that could be tested by the same test script.

    I agree that it would have been nice to get all responses in such a format; but by raising the bar that far I’m sure I’d have reduced levels of participation dramatically. I am awed that this article has provoked 635 responses (and counting), of which the majority have been programs. I think that’s only been possible the problem was stated in such a way that people could bash out a quick attempt in the environment of their choice.

    As Clay Shirky once said (of HTML), “You cannot simultaneously have mass adoption and rigor” :-)

  631. Reposting in code block.

    a is the array, s is the search number, l and u are the lower and upper index
    {source}
    def binary_search(a,s, l=0, u=nil)
    u = a.length-1 if u==nil
    x = l+(u-l)/2
    return x if s==a[x]
    return “not found” if (u-l)a[x])
    return binary_search(a,s,l,x) if (s<a[x])
    end
    {/source}

  632. Oh dam can the moderator please fix my post please

  633. Jason B, in general I can’t fix your posted code, because in the absence of a correct source-block surrounding it, it’s possible that some of what you pasted in got mangled. If you re-post, I’ll delete the old one (and this comment).

  634. Mine works. It’s yet another recursive implementation in Python (http://pastebin.com/BhSyfDtK).

  635. This took about 15 minutes to write and another 45 for testing. Coming up with good test cases is the key, and I don’t know if my tests cover all cases, but with 1 million random arrays, the program passed.

    public static int bsearch(int value, int[] array) {
            return bsearch(value, array, 0, array.length);
        }
        
        public static int bsearch(int value, int[] array, int offset, int length) {
            if (length <= 0 || offset < 0 || offset + length > array.length) {
                return -1;
            }
            int mid = offset + length / 2;
            
            if (value == array[mid]) {
                return mid;
            }
            else if (value < array[mid]) {
                return bsearch(value, array, offset, mid - offset);
            }
            else {
                return bsearch(value, array, mid + 1, offset + length - (mid + 1));
            }
        }
    
  636. There’s a bug in your article – the quote suggests you put down the column and write the code before you mention the rules (in particular the one about not testing).

    My version worked (with testing) over 10,000 random tests for items in the list, but returns a false positive when the item is not found.

  637. Here’s my take of it in Lua. Not elegant, the maths sure are wrong and fails (try with 9, for instance). Bah. I’m definitely with the majority here :D

    local input = {}
    for i = 1,100 do
    	input[i] = math.random(100)
    end
    table.sort(input)
    
    function search(input, value, from, to)
    	from = from or 1
    	to = to or #input
    	local index = math.floor( (to - from + 1) / 2) + (from - 1)
    	if from == to then
    		if input[from] == value then return value else return nil end
    	end
    	if input[index] == value then
    		return value
    	elseif input[index] > value then
    		return search(input, value, from, index + 1)
    	else
    		return search(input, value, index + 1, to)
    	end
    end
    
    -- from command-line: lua5.1 scripname.lua value_to_look_for
    local v = tonumber(arg[1]) or 11
    
    print(search(input, v))
    
  638. I tested a sample of 20 of the Python submissions here, for a reason explained in the gist: .gist table { margin-bottom: 0; } This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters """ *** OBSOLETE *** SEE FIXED FORK: http://gist.github.com/375850 Test out 20 binary-search functions harvested from https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/ See results at the bottom. """ import random def test(search): """Test that the given search function obeys the contract: If array is sorted ascending, then search(array, key) returns a j such that array[j] == key, if possible; else -1. Report whether it passes, giving a failing test case if it fails. (We don't try to interrupt infinite loops — we didn't happen to get any.)""" ok1 = test_passes(test_randomly, search) ok0 = test_passes(test_search, search) assert ok0 == ok1 # (To see if either tester beats the other) if ok0: print 'PASS: %s' % search.__name__ else: print ' : %st%s' % (test_case, search.__name__) def test_passes(test_it, function): try: test_it(function) except: return False return True test_case = "Can't happen" def test_randomly(search): global test_case for length in xrange(10): for times in xrange(100): array = random_array(length, length) array.sort() key = random.randint(–1, length) test_case = (array, key) j = search(array, key) if j == –1: assert key not in array else: assert 0 <= j < length assert key == array[j] def random_array(length, hibound): return [random.randint(0, hibound–1) for i in xrange(length)] def test_search(search): """An 'exhaustive' tester following the 0-1 principle: if a function fails on any input, we can expect it to fail on some input using just 0's and 1's. I don't know if this is a theorem as it is for sorting networks, but it happened to work here! Heh.""" for length in xrange(20): for boundary in range(length + 1): array = [0]*boundary + [1]*(length–boundary) def expect(key, lo, hi=–1): "Check that search finds key in [lo..hi) (or -1 if absent)." global test_case test_case = (array, key) if lo < hi: assert lo <= search(array, key) < hi else: assert –1 == search(array, key) expect(0, 0, boundary) expect(1, boundary, length) # And the following tests seem redundant in practice — no # extra failures found with them: # expect(-1, -1) # expect(2, -1) # Now for the harvested functions def patrick_shields(array, key): def bsearch(listy, val, index): if len(listy) == 1: if listy[0] != val: #print "ERRRRRRRRRRRRROOOOOOOOOOOOOOORRRRRRRRRRRRRRRRRRRRRR" return –1 else: return index else: new_ind = len(listy)/2 if listy[new_ind] == val: return index+new_ind elif listy[new_ind] < val: return(bsearch(listy[new_ind+1:], val, index+new_ind+1)) else: return(bsearch(listy[:new_ind], val, index)) return bsearch(array, key, 0) # Fail: Throws an error: test(patrick_shields) def ed_marshall(range, target): crange = range offset = 0 while True: ctarget = len(crange) / 2 if crange[ctarget] == target: return ctarget + offset elif crange[ctarget] > target: crange = crange[:ctarget] else: crange = crange[ctarget+1:] offset += ctarget+1 if len(crange) == 0: return –1 # Fail: Also throws an error: test(ed_marshall) def ashish_yadav(array, key): def bsearch(arr,key,start=0,end=None): if end == None: end = len(arr) – 1 if start > end: return None if start == end and arr[start] != key: return None mid = (start+end)/2 if arr[mid] == key: return mid if arr[mid] > key: return bsearch(arr,key,start,mid–1) if arr[mid] < key: return bsearch(arr,key,mid+1,end) return bsearch(array, key) # Fails: #. assert search(array, -1) == -1 test(ashish_yadav) def travis(lst, item): bottom, top = 0, len(lst) while top – bottom >= 3: mid = (top + bottom) // 2 c = cmp(item, lst[mid]) if c < 0: bottom = mid + 1 else: return True if item == lst[bottom]: return True return top – bottom == 2 and item == lst[bottom + 1] # Fails: #. assert search(array, -1) == -1 test(travis) def dan(l, n): H = len(l) – 1 L = 0 M = int(H / 2) while H – L > 0 and n != l[M]: if n > l[M]: L = M else: H = M M = int((H + L) / 2) if n == l[M]: return M return –1 # Fails: #. IndexError: list index out of range test(dan) def ben_gutierrez(l, needle): start, end = 0, len(l)–1 while start <= end: mid = (start + end) / 2 if l[mid] == needle: return mid elif l[mid] < needle: start = mid + 1 else: end = mid – 1 return –1 # Passes, but N.B. it'd need // instead of / in Python 3 # XXX is this actually correct? test(ben_gutierrez) def si(array, key): def bsearch(nums, item): while nums: mid = len(nums) / 2 if nums[mid] > item: nums = nums[:mid] elif nums[mid] < item: nums = nums[mid+1:] else: return True return False if bsearch(array, key): return array.index(key) return –1 # Passes test(si) def aaron_maxwell(array, key): def bsearch(needle, haystack): lower = 0 upper = len(haystack) – 1 idx = int(upper/2) found = haystack[idx] == needle while not found: if lower >= upper: break if needle > haystack[idx]: lower = idx + 1 else: upper = idx – 1 idx = int(.5 *(lower + upper)) found = haystack[idx] == needle if found: return idx # found it! return False # indicating item not in the list j = bsearch(key, array) return –1 if j is False else j # Fails: #. IndexError: list index out of range test(aaron_maxwell) def Max(array, key): def bsearch(srtd,x): l = len(srtd) if l == 0: return False med = srtd[l/2] #print med if med == x: return True if x < med: return bsearch(srtd[:(l/2)],x) else: return bsearch(srtd[(l/2)+1:],x) return array.index(key) if bsearch(array, key) else –1 # Pass test(Max) def clark(data, toFind): begin = 0 end = len(data) – 1 while begin < (end – 1): pivot = int(begin + (end – begin) / 2) if data[pivot] == toFind: return pivot elif data[pivot] < toFind: end = pivot if data[begin] == toFind: return begin elif data[end] == toFind: return end return –1 # Fails: #. assert boundary <= search(array, 1) < length test(clark) def martin(array, key): def bsearch(needle, slice): if len(slice) == 1: if slice[0] == needle: return needle else: return None p = len(slice) // 2 if slice[p] > needle: return bsearch(needle, slice[:p]) else: return bsearch(needle, slice[p:]) return bsearch(key, array) or –1 # Fails: #. IndexError: list index out of range test(martin) def paul(array, key): def binsearch_iterative(t, seq): """ Return index where target `t` is found in ordered sequence `seq`. If t is not found, return None. """ left = 0 right = len(seq) – 1 while right >= left: mid = (right + left) // 2 if seq[mid] == t: return mid elif seq[mid] > t: right = mid –1 continue else: left = mid +1 continue return None return binsearch_iterative(key, array) or –1 # Fails: #. assert boundary <= search(array, 1) < length test(paul) def dave_r(haystack, needle): start = 0 length = len(haystack) end = length – 1 idx = length / 2 while True: val = haystack[idx] if val == needle: return idx elif val > needle: end = idx elif val < needle: start = idx idx = start + ((end – start) / 2) # Fails: #. IndexError: list index out of range test(dave_r) def scott(array, key): from math import ceil def bsearch(needle, haystack): top = len(haystack)–1 bottom = 0 # check that needle can still possibly exist in haystack if (not haystack) or haystack[–1] < needle or haystack[0] > needle: return None # get a value for middle val middle = (len(haystack) – (len(haystack)%2)) / 2 middle_val = haystack[middle] # check value if needle == middle_val: return middle elif needle < middle_val: return bsearch(needle, haystack[:middle]) else: return middle + 1 + bsearch(needle, haystack[middle+1:]) return bsearch(key, array) or –1 # Fails: #. assert boundary <= search(array, 1) < length test(scott) def michael_fogleman(array, value): a = 0 b = len(array) – 1 while a <= b: m = (b – a) / 2 + a if array[m] == value: return m elif array[m] > value: b = m – 1 else: a = m + 1 return –1 # Pass test(michael_fogleman) def rodrigo_b(ary, val): lo = 0 hi = len(ary) oldm = –1 while True: m = int((lo+hi)/2) if oldm == m: return –1 if val < ary[m]: oldm = m lo = m else: return m # Fails: #. IndexError: list index out of range test(rodrigo_b) def jasper(array, key): def bsearch(li, val, lo=None, up=None): if lo is None or up is None: lo = 0 up = len(li) # check that the value is inside the list if val < li[lo]: return None if val > li[up – 1]: return None print li, val, lo, up # the interval to check is [lo, up), check its size if (up – lo) <= 1: if li[lo] == val: return lo else: return None # slice and dice! mid = (lo + up) / 2 if (li[mid] <= val): return bsearch(li, val, mid, up) else: return bsearch(li, val, lo, mid) return bsearch(array, key) # Fails: #. IndexError: list index out of range test(jasper) def tomas_henriquez(array, key): def bs(array,findme): if(array): array = sorted(array) len = array.__len__() pos=len/2 mid=len/2+1 while(mid): #trunk because its an integer if mid%2 and mid != 1: mid=mid/2+1 else: mid=mid/2 if (findme == array[pos]): return array[pos] elif (findme > array[pos]): pos+=mid elif (findme < len–1 or pos < 0): break return "None" j = bs(array, key) return –1 if j == "None" else j # Fails: #. assert boundary <= search(array, 1) < length test(tomas_henriquez) # Returns the index of data in inArray, or -1 if none found def yc(inArray, data): start = 0 end = len(inArray) – 1 if end < start: return –1 if data < inArray[0] or data > inArray[–1]: return –1 while (True): if (start == end): if data == inArray[start]: return start else: return –1 middle = start + ((end–start) / 2) if (data == inArray[middle]): return middle elif (data < inArray[middle]): end = middle else: start = middle + 1 # Pass test(yc) def guilherme_melo(array, key): def binary_search(a_list, elem): if not a_list: return False index = len(a_list)/2 ret = cmp(elem, a_list[index]) if ret < 0: return binary_search(a_list[0:index], elem) elif ret > 0: return binary_search(a_list[index + 1:len(a_list)], elem) else: return True return array.index(key) if binary_search(array, key) else –1 # Pass test(guilherme_melo) # Finally, the output. The ones that don't say PASS failed; the middle # column holds the first failing test case. # Of the 6 that pass, 4 of them must be O(n) since they use array slicing. # So overall, we get 2 of 20 that succeed with O(log(n)) — though I don't # actually measure the time complexity. # # I also would have included Luke Stebbing's function, which appears correct, # except it obeys a different contract; that'd make 3 of 21 submissions good. # There were a couple more I skipped for looking like too much trouble to # get into a testable format; I wouldn't bet on them raising the average. # (I had to reindent and de-HTMLize many of the others; this could conceivably # have introduced errors.) Otherwise I just picked up every Python entry until # I got tired, going at first from the start, and then from the end. # # My main motivation was to try out 'exhaustive' testing with the 0-1 # principle. It found all of the same bugs as a random test, and no more. # Neither tester uncovered any infinite loops — I wonder if there are # any they missed? Incidentally many of the failing functions here were # certified by their authors — passed their own tests. #. : ([], 0) patrick_shields #. : ([], 0) ed_marshall #. : ([], 0) ashish_yadav #. : ([], 0) travis #. : ([], 0) dan #. PASS: ben_gutierrez #. PASS: si #. : ([], 0) aaron_maxwell #. PASS: Max #. : ([1], 1) clark #. : ([], 0) martin #. : ([1], 1) paul #. : ([], 0) dave_r #. : ([1], 1) scott #. PASS: michael_fogleman #. : ([], 0) rodrigo_b #. : ([], 0) jasper #. : ([1], 1) tomas_henriquez #. PASS: yc #. PASS: guilherme_melo #. view raw binarysearchtests.py hosted with ❤ by GitHub It turned out exactly 10% of them both passed the functional test and appeared to run in O(log(n)) time.
  639. It’s cute that all 14 failing functions could get smoked out by just two test inputs: ([], 0) and ([1], 1).

    (Assuming my tester is right!)

  640. Crap, HTML owned. Just read the update…

    Python, untested, again:

    def bsearch(a, val):
        low = 0
        high = len(a) - 1
        while low <= high:
            mid = low + (high - low) / 2
            if a[mid] == val:
                return mid
            elif a[mid] > val:
                high = mid - 1
            else:
                low = mid + 1
        return None
    
  641. Oops. Sorry for using wrong source block.

    Wrote in C++. It’s compiled and executed once without further debugging.

    #include <iostream>
    using namespace std;
    
    
    
    int binarysearch (int* array, int size, int value) {
        int left = 0, right = size - 1, t;
        while ( left <= right ) {
            t = (left+right) >> 1;
            if (array[t] < value) {
                left = t + 1;
            } else if (array[t] > value) {
                right = t - 1;
            } else {
                return t;
            }
        }
        return -1;
    }
    
    int main(void) {
        int array [6] = {1, 2, 3, 4, 8, 9};
        cout << binarysearch(array, 6, 4);
    
    }
    
  642. Scala needs some love. I didn’t do full tests, but did a couple interactive tests to sanity-check the results.


    object ArrayUtils {
    /**
    * Perform a binary search on the specified array. The array is assumed to be
    * sorted using the ordering provided (after the function f is applied). This
    * means that the sort field can be easily extracted, or any conversions performed
    * prior to comparison for either sorting or comparing within the search. The
    * target can be represented in the type returned by the transform function f.
    *
    * A simple use code is represented below:
    *
    *

    * scala> val array = Array("a","b","c","d")
    * array: Array[java.lang.String] = Array(a, b, c, d)
    *
    * scala> def ident(in:String):String = in
    * ident: (in: String)String
    *
    * scala> ArrayUtils.bsearch(array, ident, scala.math.Ordering.String, "c")
    * res0: Option[Int] = Some(2)
    *
    * scala> ArrayUtils.bsearch(array, ident, scala.math.Ordering.String, "lksdfj")
    * res1: Option[Int] = None
    *

    */
    def bsearch[T,C](array:Array[T],f:(T)=>C,ordering:Ordering[C],target:C):Option[Int] = {
    bsearch(array,f,ordering,target,0,array.length-1)
    }

    /**
    * Private version of the search -- includes pointers to top and bottom of search
    * region. Note: both lower and upper bounds are inclusive.
    */
    private def bsearch[T,C](
    array:Array[T],f:(T)=>C,ordering:Ordering[C],target:C,
    lowerBound:Int, upperBound:Int):Option[Int] = {
    val pivotIndex = (lowerBound + upperBound)/2
    val pivotComparable = f(array(pivotIndex))

    if(lowerBound > upperBound) { // element not found
    None
    } else if(ordering.equiv(pivotComparable, target)) {
    Some(pivotIndex)
    } else if(ordering.gt(pivotComparable, target)) {
    bsearch(array,f,ordering,target,lowerBound,pivotIndex-1)
    } else { // must be lt
    bsearch(array,f,ordering,target,pivotIndex+1,upperBound)
    }
    }
    }

  643.         public int Search<T>(T[] array, Comparison<T> comparison, T item)
            {
                int left = 0, right = array.Length;
    
                while (left < right)
                {
                    int i = left + (right - left) / 2;
    
                    int c = comparison(item, array[i]);
    
                    if (c == 0)
                    {
                        return i;
                    }
                    else if (c < 0)
                    {
                        right = i;
                    }
                    else
                    {
                        left = i + 1;
                    }
                }
    
                return -1;
            }
    

    I only did a bit of reasoning on my fingers before committing to this version. Immediately after typing the

    left + (right - left) / 2

    I thought, “OK, I think this is where the overflow is supposed to hide” but I felt stupid as I couldn’t see it…

  644. Darius: Your tests are broken :)
    Working on a fork that fixes at least some of them…

  645. Whew, look at that backlog. Ok, here we go.

    @Rodrigo B., @nos, @David Carlson: Care to re-submit with corrected source blocks? Unfortunately I think wordpress has eaten some of your code.

    @Mike Beverley: I believe in an earlier attempt you asked for my comments? The code looks like it should work, which puts you in the minority here :). That said, I must question your use of doubles as index counters. It requires a lot more casting, as you have done, and makes it harder to reason about the safety of the code. Also, “pivot = Convert.ToInt32 ((Math.Ceiling ((((upperBound – lowerBound) / 2) + lowerBound)) – 1))” seems to be biased low. For example, in a 3-element array (lowerBound=0, upperBound=2) the pivot is chosen to be 0?

    @Martin Elwin: In the second recurse, you should probably use pivot+1 as a lower bound since you’re done with the pivot now, but other than that looks good!

    @Ewan Dawson: Your code doesn’t handle 0 length lists? Also, using list slices will make your performance O(n) instead of O(logn) for a bsearch call I believe.

    @Losky: Lol, you corrected all my comments before I even posted them – well done.

    @Scott: Looks good (I didn’t know that “not haystack” trick for empty arrays, neat!), but the standard comment about list slices being O(n) applies.

    @UnoriginalGuy: Your code does not handle empty arrays. Also, for two element arrays the pivot must be the first or the last, so “if (middle == haystack.Length-1 || middle == 0) return -1;” can cause it to fail unnecessarily searching for elements adjacent to the ends.

    @Dave R.: Your code doesn’t handle haystacks of length 0, so yep, it’s harder than it looks :). Come to think of it, you also don’t have an escape clause for not finding the element, so your code can infinite loop if the item isn’t present. Also, with “idx = start + ((end – start) / 2)” idx can be start for a two-element haystacks, so when you recurse to the right (setting start=idx) you may find yourself in another infinite loop (this time even when the item IS present).

    For the recurses you probably want end=idx-1, start=idx+1, and instead of “while True”, use “while start <= end".

    @Martin: Code looks good, although the standard comment about list slices making it O(n) instead of O(logn) applies.

    @Eris: The overflow happens if you write it as (left + right)/2, so you are safe :).

  646. Well, sorting itself worked, though I made a stupid syntax mistake in code and did not implement tricky “negative index” result.
    Guess I’m too much IDE-dependent :(

  647. @Darius Bacon: Thanks for all that effort! I was a bit worried that your test suite reported a bug in my function that I didn’t see. But I quickly realized that your wrapper around my function was broken – and likewise for several other functions you tested. As promised, I’ve forked your tests of 20 python functions here. .gist table { margin-bottom: 0; } This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters """ Test out 20 binary-search functions harvested from https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/ See results at the bottom. """ import random failures = {} def test(search): """Test that the given search function obeys the contract: If array is sorted ascending, then search(array, key) returns a j such that array[j] == key, if possible; else -1. Report whether it passes, giving a failing test case if it fails. (We don't try to interrupt infinite loops — we didn't happen to get any.)""" ok1 = test_passes(test_randomly, search) ok0 = test_passes(test_search, search) #assert ok0 == ok1 # (To see if either tester beats the other) if ok0 and ok1: print 'PASS: %s' % search.__name__ else: if ok0 or ok1: # only one of the testers passed! print "—— Only one tester passed for %s! —- " % search.__name__ print "Random failures: %s" % str(failures.get((search, 'random'))) print "Exhaustive failures: %s" % str(failures.get((search, 'exhaustive'))) print "——————————————-" else: print ' : %st%s' % (test_case, search.__name__) def test_passes(test_it, function): try: test_it(function) except: return False return True test_case = "Can't happen" def test_randomly(search): global test_case for length in xrange(10): for times in xrange(100): array = random_array(length, length) array.sort() key = random.randint(–1, length) test_case = (array, key) try: j = search(array, key) if j == –1: assert key not in array else: assert 0 <= j < length assert key == array[j] except: failures[(search, 'random')] = test_case raise def random_array(length, hibound): return [random.randint(0, hibound–1) for i in xrange(length)] def test_search(search): """An 'exhaustive' tester following the 0-1 principle: if a function fails on any input, we can expect it to fail on some input using just 0's and 1's. I don't know if this is a theorem as it is for sorting networks, but it happened to work here! Heh.""" for length in xrange(20): for boundary in range(length + 1): array = [0]*boundary + [1]*(length–boundary) def expect(key, lo, hi=–1): "Check that search finds key in [lo..hi) (or -1 if absent)." global test_case test_case = (array, key) if lo < hi: assert lo <= search(array, key) < hi else: assert –1 == search(array, key) try: expect(0, 0, boundary) expect(1, boundary, length) except: failures[(search, 'exhaustive')] = test_case raise # And the following tests seem redundant in practice — no # extra failures found with them: # expect(-1, -1) # expect(2, -1) # Now for the harvested functions def patrick_shields(array, key): def bsearch(listy, val, index): if len(listy) == 1: if listy[0] != val: #print "ERRRRRRRRRRRRROOOOOOOOOOOOOOORRRRRRRRRRRRRRRRRRRRRR" return –1 else: return index else: new_ind = len(listy)/2 if listy[new_ind] == val: return index+new_ind elif listy[new_ind] < val: return(bsearch(listy[new_ind+1:], val, index+new_ind+1)) else: return(bsearch(listy[:new_ind], val, index)) return bsearch(array, key, 0) # Fail: Throws an error: test(patrick_shields) def ed_marshall(range, target): crange = range offset = 0 while True: ctarget = len(crange) / 2 if crange[ctarget] == target: return ctarget + offset elif crange[ctarget] > target: crange = crange[:ctarget] else: crange = crange[ctarget+1:] offset += ctarget+1 if len(crange) == 0: return –1 # Fail: Also throws an error: test(ed_marshall) def ashish_yadav(array, key): def bsearch(arr,key,start=0,end=None): if end == None: end = len(arr) – 1 if start > end: return None if start == end and arr[start] != key: return None mid = (start+end)/2 if arr[mid] == key: return mid if arr[mid] > key: return bsearch(arr,key,start,mid–1) if arr[mid] < key: return bsearch(arr,key,mid+1,end) i = bsearch(array, key) return –1 if i is None else i # passes test(ashish_yadav) def travis(lst, item): bottom, top = 0, len(lst) while top – bottom >= 3: mid = (top + bottom) // 2 c = cmp(item, lst[mid]) if c < 0: bottom = mid + 1 else: return True if item == lst[bottom]: return True return top – bottom == 2 and item == lst[bottom + 1] # Fails: #. assert search(array, -1) == -1 # N.B. this one only returns success or failure, not an index; # so it's incompatible with this test suite. test(travis) def dan(l, n): H = len(l) – 1 L = 0 M = int(H / 2) while H – L > 0 and n != l[M]: if n > l[M]: L = M else: H = M M = int((H + L) / 2) if n == l[M]: return M return –1 # Fails: #. IndexError: list index out of range test(dan) def ben_gutierrez(l, needle): start, end = 0, len(l)–1 while start <= end: mid = (start + end) / 2 if l[mid] == needle: return mid elif l[mid] < needle: start = mid + 1 else: end = mid – 1 return –1 # Passes, but N.B. it'd need // instead of / in Python 3 test(ben_gutierrez) def si(array, key): def bsearch(nums, item): while nums: mid = len(nums) / 2 if nums[mid] > item: nums = nums[:mid] elif nums[mid] < item: nums = nums[mid+1:] else: return True return False if bsearch(array, key): return array.index(key) return –1 # Passes test(si) def aaron_maxwell(array, key): def bsearch(needle, haystack): lower = 0 upper = len(haystack) – 1 idx = int(upper/2) found = haystack[idx] == needle while not found: if lower >= upper: break if needle > haystack[idx]: lower = idx + 1 else: upper = idx – 1 idx = int(.5 *(lower + upper)) found = haystack[idx] == needle if found: return idx # found it! return False # indicating item not in the list j = bsearch(key, array) return –1 if j is False else j # Fails: #. IndexError: list index out of range test(aaron_maxwell) def Max(array, key): def bsearch(srtd,x): l = len(srtd) if l == 0: return False med = srtd[l/2] #print med if med == x: return True if x < med: return bsearch(srtd[:(l/2)],x) else: return bsearch(srtd[(l/2)+1:],x) return array.index(key) if bsearch(array, key) else –1 # Pass test(Max) def clark(data, toFind): begin = 0 end = len(data) – 1 while begin < (end – 1): pivot = int(begin + (end – begin) / 2) if data[pivot] == toFind: return pivot elif data[pivot] < toFind: end = pivot if data[begin] == toFind: return begin elif data[end] == toFind: return end return –1 # Fails: #. assert boundary <= search(array, 1) < length test(clark) def martin(array, key): def bsearch(needle, slice): if len(slice) == 1: if slice[0] == needle: return needle else: return None p = len(slice) // 2 if slice[p] > needle: return bsearch(needle, slice[:p]) else: return bsearch(needle, slice[p:]) i = bsearch(key, array) return –1 if i is None else i # Fails: #. IndexError: list index out of range test(martin) def paul(array, key): def binsearch_iterative(t, seq): """ Return index where target `t` is found in ordered sequence `seq`. If t is not found, return None. """ left = 0 right = len(seq) – 1 while right >= left: mid = (right + left) // 2 if seq[mid] == t: return mid elif seq[mid] > t: right = mid –1 continue else: left = mid +1 continue return None i = binsearch_iterative(key, array) return –1 if i is None else i # passes test(paul) def dave_r(array, key): def bsearch(haystack, needle): start = 0 length = len(haystack) end = length – 1 idx = length / 2 while True: val = haystack[idx] if val == needle: return idx elif val > needle: end = idx elif val < needle: start = idx idx = start + ((end – start) / 2) i = bsearch(array, key) return –1 if i is None else i # Fails: #. IndexError: list index out of range test(dave_r) def scott(array, key): from math import ceil def bsearch(needle, haystack): top = len(haystack)–1 bottom = 0 # check that needle can still possibly exist in haystack if (not haystack) or haystack[–1] < needle or haystack[0] > needle: return None # get a value for middle val middle = (len(haystack) – (len(haystack)%2)) / 2 middle_val = haystack[middle] # check value if needle == middle_val: return middle elif needle < middle_val: return bsearch(needle, haystack[:middle]) else: return middle + 1 + bsearch(needle, haystack[middle+1:]) i = bsearch(key, array) return –1 if i is None else i # Fails: # XXX Blew up the `assert ok0 == ok1` check! # This raises TypeError on recursive calls since it potentially # adds middle + 1 + None in the final `else` clause. Only caught by the random tests, not the # 'exhaustive' tests. Typical failing input has some repeated values: ([0, 0, 2], 1) test(scott) def michael_fogleman(array, value): a = 0 b = len(array) – 1 while a <= b: m = (b – a) / 2 + a if array[m] == value: return m elif array[m] > value: b = m – 1 else: a = m + 1 return –1 # Pass test(michael_fogleman) def rodrigo_b(ary, val): lo = 0 hi = len(ary) oldm = –1 while True: m = int((lo+hi)/2) if oldm == m: return –1 if val < ary[m]: oldm = m lo = m else: return m # Fails: #. IndexError: list index out of range test(rodrigo_b) def jasper(array, key): def bsearch(li, val, lo=None, up=None): if lo is None or up is None: lo = 0 up = len(li) # check that the value is inside the list if val < li[lo]: return None if val > li[up – 1]: return None print li, val, lo, up # the interval to check is [lo, up), check its size if (up – lo) <= 1: if li[lo] == val: return lo else: return None # slice and dice! mid = (lo + up) / 2 if (li[mid] <= val): return bsearch(li, val, mid, up) else: return bsearch(li, val, lo, mid) i = bsearch(array, key) return –1 if i is None else i # Fails: #. IndexError: list index out of range test(jasper) def tomas_henriquez(array, key): def bs(array,findme): if(array): array = sorted(array) len = array.__len__() pos=len/2 mid=len/2+1 while(mid): #trunk because its an integer if mid%2 and mid != 1: mid=mid/2+1 else: mid=mid/2 if (findme == array[pos]): return array[pos] elif (findme > array[pos]): pos+=mid elif (findme < len–1 or pos < 0): break return "None" j = bs(array, key) return –1 if j is "None" else j # Fails: #. assert boundary <= search(array, 1) < length test(tomas_henriquez) # Returns the index of data in inArray, or -1 if none found def yc(inArray, data): start = 0 end = len(inArray) – 1 if end < start: return –1 if data < inArray[0] or data > inArray[–1]: return –1 while (True): if (start == end): if data == inArray[start]: return start else: return –1 middle = start + ((end–start) / 2) if (data == inArray[middle]): return middle elif (data < inArray[middle]): end = middle else: start = middle + 1 # Pass test(yc) def guilherme_melo(array, key): def binary_search(a_list, elem): if not a_list: return False index = len(a_list)/2 ret = cmp(elem, a_list[index]) if ret < 0: return binary_search(a_list[0:index], elem) elif ret > 0: return binary_search(a_list[index + 1:len(a_list)], elem) else: return True return array.index(key) if binary_search(array, key) else –1 # Pass test(guilherme_melo) # Finally, the output. The ones that don't say PASS failed; the middle # column holds the first failing test case. # Of the 8 that pass, 4 of them must be O(n) since they use array slicing. # So overall, we get 4 of 20 that succeed with O(log(n)) — though I don't # actually measure the time complexity. # # I also would have included Luke Stebbing's function, which appears correct, # except it obeys a different contract; that'd make 5 of 21 submissions good. # There were a couple more I skipped for looking like too much trouble to # get into a testable format; I wouldn't bet on them raising the average. # (I had to reindent and de-HTMLize many of the others; this could conceivably # have introduced errors.) Otherwise I just picked up every Python entry until # I got tired, going at first from the start, and then from the end. # # My main motivation was to try out 'exhaustive' testing with the 0-1 # principle. It found nearly all of the same bugs as a random test, *except* one # failure in the scott() function that was only found by random tests. # Neither tester uncovered any infinite loops — I wonder if there are # any they missed? Incidentally many of the failing functions here were # certified by their authors — passed their own tests. #. : ([], 0) patrick_shields #. : ([], 0) ed_marshall #. PASS: ashish_yadav #. : ([], 0) travis #. : ([], 0) dan #. PASS: ben_gutierrez #. PASS: si #. : ([], 0) aaron_maxwell #. PASS: Max #. : ([1], 1) clark #. : ([], 0) martin #. PASS: paul #. : ([], 0) dave_r #. —— Only one tester passed for scott! —- #. Random failures: ([0, 0, 2], 1) #. Exhaustive failures: None #. ——————————————- #. PASS: michael_fogleman #. : ([], 0) rodrigo_b #. : ([], 0) jasper #. : ([1], 1) tomas_henriquez #. PASS: yc #. PASS: guilherme_melo view raw binarysearchtests.py hosted with ❤ by GitHub With the wrappers fixed, my own binsearch (“paul”) passes . As does Ashish Yadav’s. So the score goes up from 6 passing to 8 passing. Your mistake was that in several cases your wrapper function returned something like “result or -1” , which turns a successful match at index 0 into a false negative. I fixed all broken wrappers that I could see. It didn’t help everybody though :) More interesting, after doing those fixes, I discovered a bug in one function that was only found by the random tests, not the exhaustive approach. Which in turn prompted a little fiddling with the failure reporting, since in that case your use of a global `test_case` caused a more recent success to be reported instead of the real failure :) The buggy case looked like ([0, 0, 2], 1). Visual inspection of Scott’s function should reveal how it fails in that case. I see several lessons here: * tests are software, therefore tests can have bugs * exhaustive generated test cases aren’t really exhaustive if they don’t trigger code branches that are reachable by valid input * randomly generated test cases might find some edge cases you haven’t thought of * code coverage tools could have helped find this particular bug (but certainly not all the bugs) * globals are a frequent source of bugs :) This was fun!
  648. Failed — got the comparison the wrong way around. (I suppose you could say it’s still a binary search, but for data sorted high-to-low, but that seems a bit of a cop-out.)

  649. Ruby, tested before submit, seems fully-functional.
    Intended to find the lower bound, not some element.

    def bsearch (arr, elem)
      lo = 0
      hi = arr.size
      return -1 if hi == 0
      
      # search in [lo, hi)
      # end if range size is <= 1
      while lo < hi -1
        # mid = (lo + hi-1)/2
        mid = lo + (hi - lo - 1)/2
    
        case arr[mid] <=> elem
        when -1
          # arr[mid] < elem
          # search in [mid+1, hi)
          lo = mid + 1
        when 0
          # arr[mid] == elem
          # search in [lo, mid+1)
          hi = mid + 1
        when 1
          # arr[mid] > elem
          # search in [lo, mid)
          hi = mid
        end
      end
      
      return lo if arr[lo] == elem
      return -1
    end
    
  650. @Eric Burnett
    Thanks for looking at the code…! But not sure I can exclude the pivot from the upper half recurse as I don’t compare the pivot for match before recursing. In that case I’d have to also check the pivot for the match before recursing:

    def binsearch(ar, v, s=0, e=ar.length)
      return nil if s >= e
      return ar[s] == v ? s : nil if e-s == 1
    
      pivot = (e+s) / 2
    
      #new test of pivot
      return pivot if ar[pivot] == v
    
      return ar[pivot] > v ? binsearch(ar, v, s, pivot) : binsearch(ar, v, pivot+1, e)
    end
    

    Perhaps pivot was a bad choice of name as I pushed it into the upper half… :] Think it looks cleaner in the old version – one less test/return and we avoid the +1…

    Anywho – it was fun – I agree!

  651. Pingback: links for 2010-04-22 : Bob Plankers, The Lone Sysadmin

  652. {source}
    public class BinarySearch {

    public static int search(int val, int[] arr, int start, int end) {
    int half = ((end – start) / 2) + start;
    if (arr[half] == val)
    return half;

    else if (arr[half] < val && half + 1 val && half – 1 > 0)
    return search(val, arr, 0, half – 1);

    else
    return -1;
    }

    public static void main(String[] args) {
    int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    System.out.println(search(0, arr, 0, arr.length));
    System.out.println(search(10, arr, 0, arr.length));
    System.out.println(search(1, arr, 0, arr.length));
    System.out.println(search(20, arr, 0, arr.length));
    }
    }
    {/source}

  653. @Martin Elwin: Whoops, missed that! You are correct, I was too hasty for my own good. I think I like the old version better myself :P.

  654. Pingback: Are you one of the 10% of programmers who can write a binary search? « Loses weight the hall according to Iraq

  655. Wrote it in Python. Was sure sure sure that I had checked for all errors and all possibilities:
    – item not in array
    – step out of checking if length of half of array is only 0, special case checking if only one element is left
    – check if divide result is even or not even
    Result: still bombed out with “list index out of range”…

    a bit ashamed about the long code…
    def check_even(number):
    if number%2==0:
    result = True
    else:
    result = False
    return result

    def check_found(divide):
    if choice == range_list[divide]:
    # Need to indicate that we need to treat the global variable
    global range_list
    print “Found choice !”, choice, range_list
    found = True
    elif choice > range_list[divide]:
    # narrow it down to the upper part
    range_list = range_list[divide:] #do not add 1 as need extra out-of-bound checks then
    else:
    # narrow it down to the lower part
    range_list = range_list[:divide]
    length = len(range_list)
    if length == 0:
    print “Choice NOT found in list !”, choice, range_list
    if length == 1:
    if choice == range_list[0]:
    print “Found choice finally !”, choice, range_list
    else:
    print “Choice not found in list !”, choice, range_list
    #
    # MAIN
    #
    choice = raw_input(“Enter a number to search for: “)

    range_list=[1,2,3,4,5,6,7,8,9,10,11,12,13]
    length = len(range_list)
    found = False
    while not found:
    # check if len is even or oneven
    if check_even(length):
    # Even, just divide by two and check
    divide = len(range_list)/2
    check_found(divide)
    if length == 0 or length == 1:
    # Stop searching
    break
    else:
    # Number is uneven
    divide = len(range_list)/2
    # Check leftover
    if len(range_list) > 1:
    if choice == range_list[divide+1]:
    print “Found choice !”, choice, range_list
    # Check rest
    check_found(divide)
    if length == 0 or length == 1:
    # Stop searching
    break

  656. Here we are — totally untested Clojure solution (apart from copy-and-paste to REPL and making sure it does not throw an immediate exception)

    http://paste.lisp.org/display/98164

  657. Paul, thanks very much for the fixes! I should have at least warned more prominently about the chance of adding errors of my own. I’ve forked your version again to fix the travis wrapper.

  658. Another untested attempt:

    function bSearch($value, &$array, $start = 0, $end = NULL) {
    	$count = count($array);
    	if ($value < $array[0]) {
    		return FALSE;
    	}
    	if ($value > $array[$count - 1]) {
    		return FALSE;
    	}
    	$end = (!$end > 0) ? $end : $count - 1;
    	if ($start == $end) {
    		return FALSE;
    	}
    	$mid = $start + floor(($end - $start) / 2);
    	if ($array[$mid] == $value) {
    		printf("value %d is at position %d\n", $value, $mid);
    	} elseif ($array[$mid] > $value) {
    		return bSearch($value, $array, $start, $mid);
    	} else {
    		return bSearch($value, $array, $mid, $end);
    	}
    }
    
  659. Darn, the ! in line 9 is wrong :’-(

    So close…

  660. After some testing and fixes, here a supposedly working implementation in PHP:

    function bSearch($value, &$array, $start = 0, $end = NULL) {
    //	printf("value: %d, start: %d, end: %d\n", $value, $start, $end);
    	$count = count($array);
    	if ($value < $array[0]) {
    		printf("value %d below range\n", $value);
    		return FALSE;
    	}
    	if ($value > $array[$count - 1]) {
    		printf("value %d above range\n", $value);
    		return FALSE;
    	}
    	$end = ($end > 0) ? $end : $count - 1;
    	if ($start == $end) {
    		return FALSE;
    	}
    	if ((int)$end - (int)$start == 1 && $array[$end] != $value && $array[$start] != $value) {
    		printf("value %d wasn't found\n", $value);
    		return FALSE;
    	}
    	$mid = $start + (floor($end - $start) / 2);
    	if ($array[$start] == $value) {
    		printf("value %d is at position %d\n", $value, $start);
    	} elseif ($array[$end] == $value) {
    		printf("value %d is at position %d\n", $value, $end);
    	} elseif ($array[$mid] == $value) {
    		printf("value %d is at position %d\n", $value, $mid);
    		return TRUE;
    	} elseif ($array[$mid] > $value) {
    		return bSearch($value, $array, $start, $mid);
    	} else {
    		return bSearch($value, $array, $mid, $end);
    	}
    }
    
  661. I didn’t even try to get it right the first time, because I got interested in
    the specfication. Why code to a spec if it’s the wrong spec? Most solutions
    here solved for “is the item in the array?” An alternative is “Where would the
    item be if it were in the array?” In this case, the caller would have to
    check for actual membership, but the function, by returning more information,
    fits a broader range of scenarios. The return value works well in python
    because it can always be passed to list.insert.

    #! /bin/env python
    
    def bs(array, item):
        '''
        find the idx item would be at in the sorted array if it were a member of
        the array.  Caller should check that index to determine actual membership.
        '''
        length = len(array)
        idxl = 0
        idxh = length - 1
        while 1 :
            if idxh - idxl < 2:
                if not length: return 0
                if item > array[idxl]:
                    if item > array[idxh]:
                        return idxh + 1
                    return idxh
                return idxl 
            mid = sum(divmod(idxh - idxl,2)) + idxl
            if item > array[mid]: idxl = mid
            else: idxh = mid 
    

    permalink (and unit test) here:

    http://www.ynform.org/w/Pub/PythonBinarySearch

  662. Probably too much haskell for most true haskell folk

    binsearch _ [] = False
    binsearch x ls
    | x > ls !! i = binsearch x (drop i ls)
    | x < ls !! i = binsearch x (take i ls)
    | x == ls !! i = True
    where
     i = length ls `div` 2

  663. spoke too soon, that doesnt terminate on failure

    binsearch _ [] = False
    binsearch x [z]
    | x == z = True
    | otherwise = False
    binsearch x ls
    | x > ls !! i = binsearch x (drop i ls)
    | x < ls !! i = binsearch x (take i ls)
    where
    i = length ls `div` 2

  664. *sigh*

    binsearch _ [] = False
    binsearch x [z]
    | x == z = True
    | otherwise = False
    binsearch x ls
    | x >= ls !! i = binsearch x (drop i ls)
    | x < ls !! i = binsearch x (take i ls)
    where
    i = length ls `div` 2

  665. I have to admit that after a long day of work I ended up getting frustrated before I finished proofing on paper what my code was doing. in all cases (but then now days with an interpreted language that lack a substantial cost to compile we don’t program like this anymore, do we?)

    Noted in the code the two things I fixed when running my tests…

    http://pastebin.com/HDvtLGXp

  666. Only just seen this :(

    #!/usr/local/bin/lua

    bsearch = function(structure,data,begin_,end_)
    local middle = math.floor((end_ – begin_) /2)+ begin_
    if data == structure[middle] then return middle
    elseif begin_ >= end_ then return “not found”
    elseif data < structure[middle] then return bsearch(structure,data,begin_,middle-1)
    else return bsearch(structure,data,middle+1,end_)
    end
    end

  667. Joseph Copenhaver
    public static int bSearch(int sought, int[] sortedIntsAscending)
    {
       int ub=sortedIntsAscending.length-1, lb=0, idx=-1;
       int mid=ub/2;
       
       while (lb <= ub)
       {
          int candidate = sortedIntsAscending[mid];
          if (sought == candidate)
          {
             idx = mid;
             break;
          }
          else if (sought > candidate)
             mid = (ub + (lb = mid + 1))/2;
          else
             mid = (lb + (ub = mid - 1))/2;
       }
       
       return idx;
    }
    // success
    
  668. My untested c# code, will test in the morning:

    using System;
    
    public static class Search<T>
    {
        /// <summary>
        /// Finds the index of searchFor in toSearch via binary search
        /// </summary>
        /// <param name="toSearch">An ordered array to search</param>
        /// <param name="searchFor">An object to search for</param>
        /// <returns>The index of searchFor, if found. -1 if not found.</returns>
        public static int BinarySearch(IComparable<T>[] toSearch, IComparable<T> searchFor)
        {
            int startIndex;
            int endIndex;
            int midWay;
    
            startIndex = 0;
            endIndex = toSearch.Length - 1;
    
            while (endIndex > startIndex + 1)
            {
                midWay = (int)Math.Ceiling((double)(endIndex - startIndex) / 2.0) + startIndex;
                if (isInRange(ref toSearch, searchFor, startIndex, endIndex))
                {
                    endIndex = midWay;
                }
                else
                {
                    startIndex = midWay;
                }
            }
    
            if (searchFor.CompareTo((T)toSearch[startIndex]) == 0) return startIndex;
            if (searchFor.CompareTo((T)toSearch[endIndex]) == 0) return endIndex;
            return -1;
        }
    
        private static bool isInRange(ref IComparable<T>[] toSearch, IComparable<T> searchFor, int startIndex, int endIndex)
        {
            return (searchFor.CompareTo((T)toSearch[startIndex]) >= 0 
                && searchFor.CompareTo((T)toSearch[endIndex]) <= 0);
        }
    }
    
  669. Posted my C# code here:


    static int BinarySearch(int[] array, int target)
    {
    int high = array.Length – 1;
    int low = 0;
    int mid = (high + low) / 2;
    while (high >= low)
    {
    if (array[mid] == target)
    {
    return mid;
    }
    else if (array[mid] > target)
    {
    high = mid – 1;
    }
    else
    {
    low = mid + 1;
    }
    mid = (high + low) / 2;
    }
    return -1;
    }

    Seems to work after testing. Wrote a test harness to generate 10,000 arrays of varying lengths, and got passes. Had some bugs in the harness itself, but didn’t catch any in the binary search method :)

  670. Here’s my Clojure code:

    (defn binary-search
      "return the index of t in v, else nil"
      [v t]
      (loop [lo 0, hi (dec (count v))]
        (condp = (- hi lo)
          -1 nil ; v was empty
          0 (cond (= t (v lo)) lo, :else nil)
          1 (cond (= t (v lo)) lo, (= t (v hi)) hi, :else nil)
          ;else
          (let [m  (+ lo (-> hi (- lo) (/ 2) int))
                vm (v m)]
            (cond
              (= t vm) m
              (< t vm) (recur lo (dec m))
              (> t vm) (recur (inc m) hi))))))
    

    And here’s my little test suite, using the ‘is’ macro from clojure.test:

    (def bs binary-search)
    
    (is (= nil (bs [] 3)))
    
    (is (= 0   (bs [3] 3)))
    (is (nil?  (bs [3] 4)))
    (is (nil?  (bs [3] 2)))
    
    (is (= 0   (bs [3 4] 3)))
    (is (= 1   (bs [2 3] 3)))
    (is (= nil (bs [2 4] 3)))
    (is (= nil (bs [4 5] 3)))
    (is (= nil (bs [1 2] 3)))
    
    (is (= 1   (bs [2 3 4] 3)))
    (is (= 0   (bs [3 4 5] 3)))
    (is (= 2   (bs [1 2 3] 3)))
    (is (= nil (bs [4 5 6] 3)))
    (is (= nil (bs [0 1 2] 3)))
    (is (= nil (bs [0 2 4] 3)))
    (is (= nil (bs [2 4 6] 3)))
    
    (is (= 3 (bs [0 1 2 3 4 5 6 7 8 9 10] 3)))
    (is (= nil (bs [0 1 2 4 5 6 7 8 9 10] 3)))
    (is (= 9 (bs [0 1 2 3 4 5 6 7 8 9] 9)))
    (is (= 3 (bs [0 0 0 3 5 5 5 5 5 5 5 5] 3)))
    

    When I first tested I found a fatal syntax error, a misplaced parenthesis, but the logic is sound.

    I then eliminated the ‘0’ and ‘1’ cases from the ‘condp’, and, as I suspected, the tests still pass.

  671. Pingback: Are you one of the 10% of programmers who can write a binary search? « The Reinvigorated Programmer | Jake Hackl

  672. I think I got it right. Here’s my code in Haskell:

    binary :: Ord a => a -> [a] -> Bool
    binary _ [] = False
    binary n [x] = n == x
    binary n xs | n <= comp = binary n (fst sides)
                | otherwise = binary n (snd sides)
        where middle = div (length xs) 2
              comp = xs !! (middle - 1)
              sides = splitAt middle xs
    
  673. I did the test — I messed up by an off-by-one error at the top end, which I found once I did start testing, and once I fixed that it *seems* right.

    I did this in perl:

    sub binsearch
    {
    my ($targetval, $array_ref, $left, $right) = @_;
    
    if ( $left >= ($right - 1) ) {
        # we're done; return $left
        return $left;
    }
    
    my $mid = int(($left + $right) / 2);
    my $val = $array_ref->[$mid];
    if ( $val > $targetval ) {
        # val is larger than targetval -- use left half
        return binsearch($targetval, $array_ref, $left, $mid);
    }
    elsif ( $val < $targetval ) {
        # val is smaller than targetval -- use right half
        return binsearch($targetval, $array_ref, $mid, $right);
    }
    else {
        # nailed it
        return $mid;
    }
    
    return;
    }
    
  674. Pingback: Testing is not a substitute for thinking (binary search part 3) « The Reinvigorated Programmer

  675. I did it wrong the first time. Hooray for test-driven development!

  676. template< typename T >
    typename vector<T>::iterator BinarySearch(vector<T>& List, T& Elem, typename vector<T>::iterator& Begin, typename vector<T>::iterator& End)
    {
    	unsigned int ElemCount = End-Begin;
    	if(!ElemCount)
    		return List.end();
    
    	auto MidElem = Begin + (ElemCount / 2);
    
    	if(Elem == *MidElem)
    		return MidElem;
    
    	if(Elem < *MidElem)
    		return BinarySearch(List, Elem, Begin, MidElem);
    	else
    		return BinarySearch(List, Elem, ++MidElem, End);
    }
    
    
    template< typename T >
    typename vector<T>::iterator BinarySearch(vector<T>& List, T& Elem)
    {
    	return BinarySearch(List, Elem, List.begin(), List.end());
    }
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	vector<string> List;
    	List.push_back("Asizzle");
    	List.push_back("Babizzle");
    	List.push_back("Casizzle");
    	List.push_back("Dumdizzle");
    	List.push_back("Enizzle");
    	List.push_back("Fashizzle");
    	List.push_back("Glamizzle");
    
    	auto Elem = BinarySearch(List, string("Fashizzle"));
    	int Bla = Elem - List.begin();
    
    	return 0;
    }

    C++ seems to be pretty underrepresented here, so I’m fairly sure I’ve got the first C++0x solution :P

  677. I banged this out in about 20 min in ruby. Please don’t laugh at me too bad.

    a = Array.new
    
    b = 'Searching for me'
    
    c = a[(a.length-1)/2]
    i = 0
    while c != b
    puts c
    if c > b
    a = a[0..(a.length-1)/2]
    elsif c < b
    a = a[a.length/2..(a.length-1)]
    elsif c == b
    break
    end
    c = a[(a.length-1)/2]
    i += 1
    end
    
    puts "found #{i}"
    
  678. I was successful. But I’m a little surprised.

    let bsearch x a =
      if Array.length a = 0 then
        None
      else
        let rec bsearch_aux low high =
          if high <= low then
            None
          else
            let mid = low + ((high - low) / 2) in
            let mid_val = a.(mid) in
            if x < mid_val then
              bsearch_aux low mid
            else if x > mid_val then
              bsearch_aux (mid + 1) high
            else
              Some mid
        in
        bsearch_aux 0 (Array.length a)
    
  679. int binary_search(int target, int *fromlist, int listlen)
    {
    int cursor, midvalue;
    int head = -1;
    int tail = listlen;
    while(1) {
    cursor = (head+tail)/2;
    midvalue = fromlist[cursor];
    if (target==midvalue) {
    return cursor;
    } else if (1==tail-cursor) { //Task Over
    return -1;
    } else if (target<midvalue) {
    tail = cursor;
    } else {
    head = cursor;
    }
    };
    }

  680. Andrew Parker

    Here is my perl solution. Recursive and tail-call optimized. There are tests in there, but I never ran them until I thought it was correct, they were my placeholders of cases that I ran through by hand.

    DO NOT RUN THIS!! It is a very non-optimal solution (all of the array slicing is wasteful, and the passing around of arrays instead of refs probably kills too). It consumed 15 GB on my machine and took a long time to complete, but they all passed. If you take out the last few that check arrays of 50 million elements it doesn’t take long at all.

    use Test::More tests => 14;
    
    use strict;
    
    sub bsearch {
        my ($value, @list) = @_;
        return 0 if !@list;
    
        my $mid_point = int(@list/2);
        if ($mid_point) {
            my @left = @list[0..$mid_point-1];
            my @right = @list[$mid_point..$#list];
            my $mid = shift @right;
    
            if ($value == $mid) {
                return 1;
            }
    
            @_ = ($value, $value < $mid ? @left : @right);
            goto \&bsearch;
        } else {
            return $list[0] == $value;
        }
    }
    
    ok(!bsearch(1));
    ok(!bsearch(1, 2));
    ok(bsearch(2, 2));
    ok(bsearch(2, 1, 2));
    ok(bsearch(1, 1, 2, 3));
    ok(bsearch(2, 1, 2, 3));
    ok(bsearch(3, 1, 2, 3));
    ok(!bsearch(4, 1, 2, 3));
    ok(!bsearch(0, 1, 2, 3));
    
    ok(bsearch(982342, 1..50_000_000));
    ok(bsearch(50_000_000, 1..50_000_000));
    ok(bsearch(1, 1..50_000_000));
    ok(!bsearch(50_000_000 + 1, 1..50_000_000));
    ok(!bsearch(0, 1..50_000_000));
    
  681. I failed.

  682. Untested and not yet run python:

    def bsearch_core(lat, target, begin, end):
        # empty range
        if begin==end:
            return None
        # one element
        if begin==end-1:
            if lat[begin]==target:
                return begin
            else:
                return None
        pivot=(begin+end)/2
        if target<lat[pivot]:
            return bsearch_core(lat, target, begin, pivot)
        else:
            return bsearch_core(lat, target, pivot, end)
    
    def bsearch(lat, target):
        if lat is None:
            return None
        return bsearch_core(lat, target, 0, len(target))
    
  683. “NO TESTING until after you’ve decided your program is correct.”
    This means you’re testing ability to reason about code more than ability to eventually come up with correct code. It’s an important ability, but testing it puts people who are used to relying on their tests at a fairly strong disadvantage. (If you gave us a black box, told us it was a buggy binary search, and asked us to build tests to identify the bugs, I’d’ve definitely failed that because I would be working against a symmetric disadvantage.)
    When the first edition of Programming Pearls was written, this disadvantage wasn’t nearly as artificial as it is these days, with lots of cheap computrons on every programmer’s desk (and even in most of their pockets) to run the tests.
    (Note that I’m not claiming that reasoning about code isn’t still an important skill; it’s just been demoted from “essential” to merely “important”.)

    My attempt: Spent a bit over half an hour, caught no less than three bugs desk-checking, but passed all my tests (an exhaustive list of arrays with not more than five elements bounded between/including 0 and 15, searching for every value in or just outside the range the array covers) without further modifications.

    /*Requires arr to be sorted in increasing order.
      Returns the index of an array element equal to want, or -1 if no such
        element exists.
    */
    int bin_search(int *arr,size_t num,int want)
    {
    	size_t lo=0;
    	size_t hi=num-1;
    
    	/*Check the invariant we want for the loop and early-exit if
    	    it won't hold (since checking that it holds will give us
    	    the information we need to return anyways)
    	*/
    	if(num==0)
    		return -1;
    	if(arr[lo] > want)
    		return -1;
    	else if(arr[lo]==want)
    		return (int)lo;
    	else if(arr[hi] < want)
    		return -1;
    	else if(arr[hi] == want)
    		return (int)hi;
    
    	/*Invariant:  arr[lo] < want < arr[hi]*/
    	/*We get mid=lo from the truncating integer division if hi=lo+1.
    	  This will put us in an infinite loop, since arr[mid]<want,
    	    and in that case we want to move lo up to mid.
    	  (We get a symmetrical problem if we do ceil((hi-lo)/2).)
    	  So we have to fall out of the loop if we've squeezed the
    	    range strictly between lo and hi down to empty, and this
    	    is what motivates the strict inequality in the invariant.
    	*/
    	while(hi-lo > 1)
    	{
    		size_t mid=lo+(hi-lo)/2;
    
    		assert(arr[lo] < want);
    		assert(arr[hi] > want);
    		assert(lo<mid);
    		assert(mid<hi);
    
    		if(arr[mid]==want)	/*found it*/
    			return (int)mid;
    		else if(arr[mid] < want)
    			lo=mid;
    		else	/*arr[mid] > want*/
    			hi=mid;
    	}
    	assert(arr[lo]<want);
    	assert(arr[hi]>want);
    	assert(lo+1==hi);
    	return -1;
    }
    
  684. dave says:

    “NO TESTING until after you’ve decided your program is correct.”
    This means you’re testing ability to reason about code more than ability to eventually come up with correct code.

    Yes, exactly right. I knew this all along, but stupidly I never stated it clearly, which I guess is why so many people objected to the rules laid down for this challenge. Thanks for nailing it down for me: if I ever do this again, I’ll try to make this point, explicitly, right up front.

    Read on to the subsequent articles for more attempts to articulate this.

    Anyway, congratulations on passing your tests first time out. You are a ten-percenter! (It’s nice to see the asserts in your code, too: I’ve been surprised how few of the submitted functions have included these.)

  685. +1 for successful attempts, I’ve tested my code this morning and run it through Darius’s test program and it works.

  686. Although it the last line should be len(lat) not len (target), but I excused myself that on the grounds that it was a typo and not an error in the algorithm. Flame away!

  687. Sorry for the previous comment

    function search(a, T){
            var startIndex = 0;
            var endIndex = a.length - 1;
            var middleIndex;
        
            if(a.length === 0 || 
               T < a[startIndex] || 
               T > a[endIndex]) 
               return false; // empty array or T out of array boundaries
        
            while (startIndex - endIndex >= 2){
                middleIndex = startIndex + Math.floor((endIndex - startIndex)/2);
                
                if(T < a[middleIndex])
                    endIndex = middleIndex -1;
                else 
                    startIndex = middleIndex;
            }
        
            // startIndex - endIndex is in {0,1}
            return (a[startIndex] === T || a[endIndex] === T);
        }
    
  688. One bug in my code : in the while condition, I inversed startIndex and endIndex. Feel so noob…

    When fixed and adjust to run with Darius’ test code (return null when not found and the index if found), it works perfectly fine.

  689. Really great post Mike! I loved your one about K & R, but this one tops it. I realise I am obviously a little late reading the article, but I couldn’t resist the challenge, so I’ve posted my results below. Keep writing these great articles!

    Here’s my attempt in python. I got the algorithm right in the first try – except that I initially forgot to import the `math` module!
    Worth mentioning that I made silly mistakes like (max – min) / 2 in finding the average, and only spotted those after mentally tracing the execution of the code – that doesn’t count as testing, right?

    import math
    
    def bin_search(arr, target, min, max):
        if ((max < min) | (min + 1 > len(arr))):
            return -1 #target is not in array
        mid = int(math.floor((max + min) / 2))
        curr = arr[mid]
        if (target == curr):
            return mid
        elif (target < curr):
            return bin_search(arr, target, 0, mid-1)
        else: # if (target > curr):
            return bin_search(arr, target, mid+1, max)
    

    I used the following code to verify the validity of my function, using 3 dfferent arrays. One with ad odd number of elements, another with even, and finally one that was empty.

    array = [1,3,5,7,101,111,121,131] #test on array with odd number of elements
    print bin_search(array,1,0,7)
    print bin_search(array,3,0,7)
    print bin_search(array,5,0,7)
    print bin_search(array,7,0,7)
    print bin_search(array,101,0,7)
    print bin_search(array,111,0,7)
    print bin_search(array,121,0,7)
    print bin_search(array,131,0,7)
    print bin_search(array,999,0,7) # should be -1 as item is not in list
    
    array = [1,3,5,7,101,111,121,131,141] #test on array with even number of elements
    print bin_search(array,1,0,8)
    print bin_search(array,3,0,8)
    print bin_search(array,5,0,8)
    print bin_search(array,7,0,8)
    print bin_search(array,101,0,8)
    print bin_search(array,111,0,8)
    print bin_search(array,121,0,8)
    print bin_search(array,131,0,8)
    print bin_search(array,141,0,8)
    print bin_search(array,999,0,8) # should be -1 as item is not in list
    
    array = []
    print bin_search(array,999,0,0) #should be -1 as array is empty
    

    And here’s the output

    >>> 
    0
    1
    2
    3
    4
    5
    6
    7
    -1
    0
    1
    2
    3
    4
    5
    6
    7
    8
    -1
    -1
    
  690. I read your posts out of order (so I knew to look out for the base cases), used Corman Common Lisp, didn’t follow the rules, and didn’t find any bugs on testing, and found some slack from a missing 1+ which would make it marginally more efficient.

    For all those Common Lisp programmers out there, please ‘know’ your functions – floor takes a second argument.

    (defun bs-helper (start end item array &optional (predicate #'<) (test #'=))
      (let ((span (- end start)))
        (cond ((<= span 0) nil)
              ((= span 1) (if (funcall test item (aref array start))
                start nil))
            (t (let ((mid (+ start (floor span 2))))
                (cond
                  ((funcall test item (aref array mid))
                    mid)
                  ((funcall predicate item (aref array mid))
                    (bs-helper start mid item array predicate test))
                  (t (bs-helper (1+ mid) end item array predicate test))))))))
    
    (defun binary-search (item array &optional (predicate #'<) (test #'=))
      "Returns the index at which item can be found, or nil if not found."
      (bs-helper 0 (length array) item array predicate test))
    
  691. Untested…fingers crossed.

    def binsearch(a, x):
    if len(a) == 0:
    return -1

    x,y = 0,len(a)-1
    while x n:
    x = mid + 1
    elif a[mid] < n:
    y = mid
    else:
    return mid

    if a[x] == n:
    return x

    return -1

  692. Apparently I’m not one of the 10%, mine didn’t work without testing. I’ve written a binary search before. It probably didn’t work the first time either (though I certainly wasn’t constraining myself to not testing it that time.)

    def bsearch(array, val):
        start = 0
        end = len(array) - 1
    
        while end > start:
            split = ((end - start) / 2)
    
            if array[split] == val:
                return True
    
            if array[split] < val:
                start = split + 1
            elif array[split] > val:
                end = split - 1
    
        return array[start] == val
    

    The correct version says “split = start + ((end – start) / 2)” at the top of the loop. Sigh.

  693. Close…but not quite. Besides the typo in the function def (should be “def binsearch(a,n):), I got the mixed up. Not a bad attempt though.

  694. Here’s my attempt on binary search experiment after three compillations and 25 minutes later » http://2vtz.sl.pt

  695. C++. As yet untested. Returns the index of the item or -1 if it can’t find it.

    Should work with any type that has an operator == and < assuming array is well ordered.

    template
    int bsearch(const T* array, const T& item, int length)
    {
    if (array == NULL || length = 1)
    {
    int mid = ((end-start) >> 1) + start;
    if (array[mid] == item) return mid;
    if (item > array[mid])
    {
    start = mid + 1;
    }
    else
    {
    end = mid;
    }
    }
    return -1;
    }

    I shall now test it and find the ways in which is it bust.

  696. Pingback: My Binary Search Implementation « One line of code

  697. 	public static int bsearch(int t, int[] array) {
    		int low=0, high=array.length-1;
    		do {
    			int mid = low + (high-low) / 2;
    			if(mid < low || mid > high) break;
    			if(t < array[mid]) {
    				high = (high == mid) ? high - 1 : mid;
    			} else if(t > array[mid]) {
    				low = (low == mid) ? low + 1 : mid;
    			} else return mid;
    		} while (low<=high);
    		return -1;
    	}
    

    Assumes valid array (non-null) and no duplicate values. Playing a bit safe with the termination conditions.

  698. Doh. Fixed markup. Ruby.

    def bsearch(a, q)
      size         = a.size
      middle_index = size / 2
      return nil if a.empty?
      return middle_index if a[middle_index] == q
      return bsearch(a.first(middle_index), q) if q < a[middle_index]
      return bsearch(a.last(middle_index), q) if q > a[middle_index]
    end
    
  699. Late to the party but here’s my PHP attempt:

    http://www.pastie.org/934553

    Hope it’s bug-free! Took me half an hour.

  700. Fixing markup.

    const int *binary_search(int value, const int *begin, const int *end) {
        const int *middle = begin + (end-begin)/2;
    
        /* begin and end must not be NULL. */
        assert(begin);
        assert(end);
    
        if(begin == end)
            return NULL;
        else if(*middle < value)
            return binary_search(value, middle+1, end);
        else if(*middle > value)
            return binary_search(value, begin, middle);
        else
            return middle;
    }
    
  701. I believe mine works. The only exception is if the list has 0 size, which, in hindsight, is probably something I should check for. Although I did explicitly assume the given indices should be in the list. This took about 1.5 hours, including writing the tests.

    I also accidentally did the correct calculation for the middle value that wouldn’t cause an overflow, although I think this is irrelevant for Python which I believe supports arbitrarily large integers (or is that just Python 3?).

    Here’s the Python 2.6 code:

    {script}
    #assume list is sorted in ascending order
    #assume if x occurs, it occurs only once.
    #otherwise, we only return one of the places where it occurs.
    #assume bottom<=top
    #assume bottom, top are both in mylist
    def bsearch(mylist,x,bottom,top):
    if xmylist[top]:
    return -1
    middle=int((top-bottom)/2+bottom)
    if x==mylist[middle]:
    return middle
    if x==mylist[bottom]:
    return bottom
    if x==mylist[top]:
    return top
    if x<mylist[middle]:
    if middlemylist[middle]
    if middle>=top-1:
    return -1
    return bsearch(mylist,x,middle,top)

    #middle of list
    mylist=[0,3,30,31,32,33,500]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #start of list
    mylist=[30,31,32,33,500]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #end of list
    mylist=[1,2,3,4,5,6,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #not in list at all
    mylist=[0,1,2,3,4]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #tiny list (note that it fails for list of size 0)
    mylist=[30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #tiny list
    mylist=[1]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[20,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[30,40]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[35,40]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[30,31,32]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[29,30,32]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[28,29,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    #small lists
    mylist=[28,29,31]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)

    {/script}

    And the output:

    {script}
    [0, 3, 30, 31, 32, 33, 500]
    2
    [30, 31, 32, 33, 500]
    0
    [1, 2, 3, 4, 5, 6, 30]
    6
    [0, 1, 2, 3, 4]
    -1
    [30]
    0
    [1]
    -1
    [20, 30]
    1
    [30, 40]
    0
    [35, 40]
    -1
    [30, 31, 32]
    0
    [29, 30, 32]
    1
    [28, 29, 30]
    2
    [28, 29, 31]
    -1

    {/script}

  702. Fixing markup, hopefully I do this right this time. (Kind of important in Python where tabs matter):

    #assume list is sorted in ascending order
    #assume if x occurs, it occurs only once.
    #otherwise, we only return one of the places where it occurs.
    #assume bottom<=top
    #assume bottom, top are both in mylist
    def bsearch(mylist,x,bottom,top):
    	if x<mylist[bottom]:
    		return -1
    	if x>mylist[top]:
    		return -1
    	middle=int((top-bottom)/2+bottom)
    	if x==mylist[middle]:
    		return middle
    	if x==mylist[bottom]:
    		return bottom
    	if x==mylist[top]:
    		return top
    	if x<mylist[middle]:
    		if middle<=bottom+1:
    			return -1
    		return bsearch(mylist,x,bottom,middle)
    	#if we've reached here, then x>mylist[middle]
    	if middle>=top-1:
    		return -1
    	return bsearch(mylist,x,middle,top)
    
    #middle of list
    mylist=[0,3,30,31,32,33,500]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #start of list
    mylist=[30,31,32,33,500]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #end of list
    mylist=[1,2,3,4,5,6,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #not in list at all
    mylist=[0,1,2,3,4]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #tiny list (note that it fails for list of size 0)
    mylist=[30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #tiny list
    mylist=[1]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[20,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[30,40]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[35,40]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[30,31,32]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[29,30,32]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[28,29,30]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
    #small lists
    mylist=[28,29,31]
    print mylist
    print bsearch(mylist,30,0,len(mylist)-1)
    
  703. Very late to the game but

    def binarysearch(t, list, lo=0, hi=list.length-1)
    
      return nil if (hi < lo)
    
      m = (hi-lo)/2+lo
    
      if (t == list[m])
        m
      elsif (t > list[m])
        binarysearch(t, list, m+1, hi)
      elsif (t < list[m])
        binarysearch(t, list, lo, m-1)
      end
    end
    
  704. Pingback: Writing correct code, part 1: invariants (binary search part 4a) « The Reinvigorated Programmer

  705. Uhh… I tried and failed. Sorry if the response is too late.

  706. /**
       search for i in d, assumed to be a sorted list of s items
       @param d the sorted list
       @param s the number of elements in d
       @param i the item to search for
       @return true if i is in d, false otherwise
     */
    template <typename T>
    bool bsearch(const T* d, size_t s, const T& i)
    {
        if (!d)
        {
            return false;
        }
    
        // search range - [l; h[
        size_t l = 0;
        size_t h = s;
    
        while (l < h)
        {
            const size_t m = (l + h) >> 1;
    
            const T& e = d[m];
    
            if (e > i)
            {
                h = m;
            }
            else if (e < i)
            {
                l = m + 1;
            }
            else // e == i
            {
                return true;
            }
        }
    
        return l < s && d[l] == i;
    }
    
  707. Pingback: Binary Search y’all. | Meta | ateM

  708. Took me 10 minutes with a single visit to Python shell to confirm my understanding of integer division behavior in Python.

    #!/usr/bin/env python
    import sys
    
    def bsearch(lst, a):
        """
        >>> bsearch([1], 1)
        0
        >>> bsearch([1], 3)
        >>> bsearch([1,2], 1)
        0
        >>> bsearch([1,2], 2)
        1
        >>> bsearch([1,2,3], 2)
        1
        >>> bsearch([1,2,3], 4)
        >>> bsearch([1,2,3], 3)
        2
        >>> bsearch([1,2,3,4,5,6,7,8,9,10,11,12,13,14,17], 3)
        2
        >>> bsearch([1,2,3,4,5,6,7,8,9,10,11,12,13,14,17], 17)
        14
        >>> bsearch([1,2,3,4,5,6,7,8,9,10,11,12,13,14,17], 14)
        13
        >>> bsearch([1,2,3,4,5,6,7,8,9,10,11,12,13,14,17], 15)
        """
        print >> sys.stderr, "bsearch(%r, %r)" % (lst, a)
        i1, i2 = 0, len(lst) - 1
        while True:
            if i1 == i2:
                return i1 if lst[i1] == a else None
    
            mid = (i2 - i1)/2 + i1
            print >> sys.stderr, "testing mid=%d" % mid
            if lst[mid] == a:
                return mid
            elif lst[mid] > a:
                i2 = mid - 1
            else:
                i1 = mid + 1
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    
    
  709. def search(haystack, needle):
    	left_range, right_range = 0, len(haystack)-1	
    	while right_range >= left_range:
    		test_pos = int((right_range - left_range + 1)/ 2) + left_range
    		if needle == haystack[test_pos]:
    			return test_pos
    		if needle < haystack[test_pos]:
    			right_range = test_pos - 1
    		else:
    			left_range = test_pos + 1				
    	raise Exception, "Not found"
    
    if __name__ == '__main__':
    	haystack = [-100,0,20,20.5]
    	print search(haystack, 20.5)
    
  710. 1st attempt, in Python (immune to the overflow error – integers are of arbitrary precision), about 10 minutes.

    I think the restriction on not testing is a silly one. Given that testing is now widely available and understood, it makes more sense for people to use it in exercises like this. If that makes the exercise too easy, then make it harder! The use of testing has empowered us to write better code, in absolute terms, and exercises such as this should reflect that.

    That said, I have done no testing:

    #!/usr/bin/env python
    
    from random import randint
    
    def binary_search(haystack, needle):
        lower = 0
        upper = len(haystack)
        while upper > lower:
            middle = (lower + upper) / 2
            if haystack[middle] < needle:
                lower = middle + 1
            elif haystack[middle] > needle:
                upper = middle - 1
            else:
                return middle
    
    seq = sorted(randint(0, 99) for _ in xrange(50))
    print binary_search(seq, 22)
    
  711. FAIL! Two fixes required after testing:

    [/source]
    #!/usr/bin/env python

    from random import randint

    def binary_search(haystack, needle):
    lower = 0
    upper = len(haystack) – 1
    while upper >= lower:
    middle = (lower + upper) / 2
    if haystack[middle] needle:
    upper = middle – 1
    else:
    return middle

    seq = sorted(randint(0, 99) for _ in xrange(50))
    print binary_search(seq, 11)
    [/source]

    And incidentally, I haven’t seen other people posting this, but I think it’s important to think about what tests are being used to judge correctness. Mine are:

    from unittest2 import TestCase
    
    from binary_search import binary_search
    
    
    class BinarySearchTest(TestCase):
    
        def testMiddle(self):
            self.assertEquals(
                binary_search([11, 22, 33], 22),
                1)
    
        def testStart(self):
            self.assertEquals(
                binary_search([11, 22, 33], 11),
                0)
    
        def testEnd(self):
            self.assertEquals(
                binary_search([11, 22, 33], 33),
                2)
    
        def testNotPresent(self):
            self.assertEquals(
                binary_search([11, 22, 33], 9),
                None)
    
            self.assertEquals(
                binary_search([11, 22, 33], 25),
                None)
    
            self.assertEquals(
                binary_search([11, 22, 33], 44),
                None)
    
        def testEmpty(self):
            self.assertEquals(
                binary_search([], 22),
                None)
    
  712. Sigh. Scratch my previous comment. Broken source tags, plus the ‘two bugs’ I allegedly fixed after testing were, on reflection, more simply fixed using a single change instead. So I’m claiming only one bug in my original implementation. :-) My final code:

    def binary_search(haystack, needle):
        lower = 0
        upper = len(haystack)
        while upper > lower:
            middle = (lower + upper) / 2
            if haystack[middle] < needle:
                lower = middle + 1
            elif haystack[middle] > needle:
                upper = middle
            else:
                return middle
    
  713. Joel Rosario

    def binary_search(list, element)
    return false if list.length == 0

    candidate_pos = list.length / 2
    candidate_element = list[candidate_pos]

    return true if candidate_element == element

    if element > candidate_element
    binary_search(list[(candidate_pos + 1)…list.length], element)
    else
    binary_search(list[0…candidate_pos], element)
    end
    end

    def run_test(list, element, expected_result)
    print “Searching for #{element}: ”
    result = binary_search(list, element)
    print (result ? “Found” : “Not found”)
    print “….”
    puts (result == expected_result ? “OK” : “FAILED”)
    end

    list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

    print “Searching array ”
    print list.join(‘, ‘)

    run_test(list, 8, false)
    run_test(list, 9, true)
    run_test(list, 20, false)
    run_test(list, 3, true)
    run_test(list, 1, true)
    run_test(list, 5, true)
    run_test(list, 6, false)

    # This isn’t an efficient implementation at all. I wrote the binary search function without testing, but added in a test harness later to be able to check it’s results more conveniently.

  714. Well… I got a little obsessive on this one.
    My first try (2 methods) were “correct” in what they found, but not O(Log(N)) (I was using ruby’s Array#slice… damn). So I dug a lot deeper with benchmarks and tests… here are my results:


    # user system total real
    # real_bsearch best-case 0.370000 0.000000 0.370000 ( 0.367332)
    # real_bsearch worst-case 6.590000 0.000000 6.590000 ( 6.602298)
    # real_recursive_bsearch best-case 0.200000 0.000000 0.200000 ( 0.195659)
    # real_recursive_bsearch worst-case 6.950000 0.010000 6.960000 ( 6.958505)
    # bsearch best-case 12.000000 0.180000 12.180000 ( 12.200769)
    # bsearch worst-case 12.670000 0.180000 12.850000 ( 12.890500)
    # recursive_bsearch best-case 32.840000 0.050000 32.890000 ( 32.988642)
    # recursive_bsearch worst-case 35.670000 0.100000 35.770000 ( 37.023431)
    require 'benchmark'
    n = 5_000_000
    ar = (1..n).to_a
    Benchmark.bm do |x|
    %w[real_bsearch real_recursive_bsearch bsearch recursive_bsearch].each do |meth|
    x.report("#{meth} best-case") { 100_000.times do; send(meth, n/2, ar); end }
    x.report("#{meth} worst-case") { 100_000.times do; send(meth,1, ar); end }
    end
    end

    view raw

    benchmark.rb

    hosted with ❤ by GitHub


    def bsearch(needle, haystack)
    return nil unless needle && haystack
    return nil unless %w![] empty? each!.all? {|op| haystack.respond_to?(op)}
    return nil unless needle.respond_to?("<=>")
    return nil if haystack.empty?
    valid_range = haystack
    loop do
    return false if valid_range.empty?
    mid = valid_range.size / 2
    return needle if valid_range[mid] == needle
    r = valid_range[mid] > needle ? 0mid : mid+1..-1
    valid_range = valid_range[r]
    end
    end

    view raw

    bsearch.rb

    hosted with ❤ by GitHub


    def real_bsearch(needle, haystack)
    left, right = [0, haystack.size1]
    loop do
    return false if right < left
    mid = (rightleft) / 2 + left
    return needle if haystack[mid] == needle
    (left, right) = haystack[mid] > needle ? [left, mid1] : [mid + 1, right]
    end
    end


    def real_recursive_bsearch(needle, haystack, left=0, right=haystack.size1)
    return false if right < left
    mid = (rightleft) / 2 + left
    return needle if haystack[mid] == needle
    (left, right) = haystack[mid] > needle ? [left, mid1] : [mid + 1, right]
    real_recursive_bsearch(needle, haystack, left, right)
    end


    def recursive_bsearch(needle, haystack)
    return nil unless needle && haystack
    return nil unless %w![] empty? each!.all? {|op| haystack.respond_to?(op)}
    return nil unless needle.respond_to?("<=>")
    return false if haystack.empty?
    mid = haystack.size / 2
    return needle if haystack[mid] == needle
    range = haystack[mid] > needle ? 0mid : mid+1..-1
    return recursive_bsearch(needle, haystack[range])
    end

    view raw

    r_bsearch.rb

    hosted with ❤ by GitHub


    # Loaded suite /Users/kastner/Development/Ruby/junk/bsearch
    # Started
    # ….
    # Finished in 1.770907 seconds.
    #
    # 4 tests, 16412 assertions, 0 failures, 0 errors
    require 'test/unit'
    METHODS = %w!bsearch recursive_bsearch real_bsearch real_recursive_bsearch!
    class TestIt < Test::Unit::TestCase
    def assert_all(n, h, m="")
    METHODS.each do |meth|
    assert send(meth, n, h) == n, m
    end
    end
    def assert_none(n, h, m="")
    METHODS.each do |meth|
    assert !send(meth, n, h), m
    end
    end
    def test_simple
    assert_all 1, [1, 2, 3]
    end
    def test_empty
    assert_none 1, []
    end
    def test_three_elements
    assert_all 1, [1, 2, 3]
    assert_all 2, [1, 2, 3]
    assert_all 3, [1, 2, 3]
    assert_none 4, [1, 2, 3]
    assert_none1, [1, 2, 3]
    end
    def test_lots
    tests = `curl -s http://www.mac-guyver.com/switham/2010/04/Binary_Search/tests.txt.gz | zcat`
    tests.scan(/(P.+?)\n(.+?)\nin \[\n([^\]]*)\]\? (yes|no)/m).each do |m|
    method = m[1] == "yes" ? :assert_all : :assert_none
    send method, m[1].to_i, m[2].split("\n").map {|l| l.to_i}, m[0]
    end
    end
    end

    view raw

    tests.rb

    hosted with ❤ by GitHub

    and my post on the subject:
    http://metaatem.net/2010/04/25/binary-search-yall

  715. int binarySearch(int array,int high,int low,int key)
    {
    int mid=mid+(high-low)/2
    if(a[mid]=key)return mid;
    if(a[mid]key) return
    binarySearch(array,low,mid-1,key);
    }

  716. @Christopher Oliver: Binary searching is usually used for arrays or vectors or other contiguous storage, where insertion at any point other than the end is not efficient.

    Therefore, having the function return an intended insertion point is not useful (unless you’re only adding one item). Usually, the right thing to do is to append the elements you want into the vector, then sort it after all the insertions are done.

    If you are in the situation where you frequently have to add one element at a time, while keeping elements sorted, then I dare say a vector is not the right data structure, and a binary search of the sort described in this blog post would not be used.

    In short, I feel that in this case, a function that either returns the location of the found element, or else some kind of “not found” indicator, is plenty sufficient.

  717. Seems to work with a few quick tests. Ruby btw.

    def bsearch(arr, el)
      while arr.length > 1
        # find center (round down)
        split_point = arr.length / 2
    
        if arr[split_point] > el
          # centre more the el so look in lower half
          arr.slice!(split_point, arr.length)
        elsif arr[split_point] < el
          # centre less than el so look in upper half
          arr.slice!(0, split_point)
        else
          # match, woop!
          return true
        end
      end
    
      return arr[0] == el
    end
    
  718. Hmm, upon further reading I have discovered this isn’t actually a proper binary search as it doesn’t return the element, however it does fulfil the specification set above :P

  719. worked the first time, but failed to check for value outside the range, added it afterwards

    def bsearch(what, where, start=0, end=0):
        if what > where[end-1]:
            return -1
    
        mid = int((end + start)/2.0)
    
        if what == where[mid]:
            return mid
            
        if what < where[mid]:
            return bsearch(what, where, start, mid)
        return bsearch(what, where, mid, end)
    
    print bsearch(what, where, 0, len(where))
    

    tail recursive version worked from the first try, but then again it was the second implementation:

    def bsearch(what, where, start=0, end=0):
        mid = int((end + start)/2.0)
    
        if what == where[mid]:
            return mid
            
        if what < where[mid]:
            return bsearch(what, where, start, mid)
        return bsearch(what, where, mid, end)
    
  720. um, scratch the 2nd source, this is what I intended to paste:

    def bsearch(what, where):
        if what > where[-1]:
            return -1
    
        def do_search(start, end):
            mid = int((end + start)/2.0)
    
            if what == where[mid]:
                return mid
    
            if what < where[mid]:
                end = mid
            else:
                start = mid
            return do_search(start, end)
    
        return do_search(0, len(where))
    
  721. ooh, ooh! I still fail for a zero-length array :D

  722. My code..hope it works!

    	public static int binarySearch(int[] a, int value) {
    		int first=0;
    		int last = a.length - 1;
    		while (first <= last) {
    			int mid = (first + last)>>>1;
    			int midVal = a[mid];
    			if (midVal < value){
    				first = mid + 1;
    			}else if (midVal > value){
    				last = mid - 1;
    			}else 
    				return mid;
    			}
    			return -1;
    		}
    	}
    
  723. (god f*in damn you, why is there no preview)

    written with pencil and paper just now, not tested:

    int binsearch(int T, int *p, int n) {
      return bisect(T,p,0,n);
    }
    
    int bisect(int T, int *p, int a, int b) {
      int i;
      int n = b-a;
    
      if(n<=0) {
        return -1;   // not found
      } else {
        i = a + n/2;
        x = p[i];
    
        if(x<T) {
          return bisect(T,p,i+1,b);
        else if(x>T) {
          return bisect(T,p,a,i);
        } else {   // found
            return i;
        }
      }
    }
    
  724. Wow, looks like the comments will only stop coming in after every programmer in the world has had a go.

  725. No, I am not. My initial code is:

    (def binsearch (arr val (o rstart 0) (o rend (- (len arr) 1)))
      (when arr
        (if (is rstart rend)
    	(when (is arr.rstart val)
    	  rstart)
    	(let mid (trunc (/ (+ rstart rend) 2))
    	     (if (< val arr.mid)
    		 (binsearch arr val rstart mid)
    		 (is val arr.mid)
    		 mid
    		 (binsearch arr val mid rend))))))

    Check out my bitbucket repo (http://bitbucket.org/zck/binsearch) for fixes.

  726. def find_needle(needle,haystack,offset=0):
        mid_index = len(haystack)/2
        mid_value = haystack[mid_index]
        if needle == mid_value:
            return mid_index+offset
        if  0 == mid_index:
            return None
        if needle > mid_value:
            last_index = len(haystack)
            if not len(haystack[mid_index:last_index]):
                return None
            else:
                return find_needle(needle,haystack[mid_index:last_index],offset+mid_index)
        if needle < mid_value:
            return find_needle(needle,haystack[0:mid_index],offset)
    
  727. int search(int k, int b, int len)
    {
    	if (len < 3)
    	{
    		int p = b;
    		for (;p < b + len; p++)
    		{
    			if (k == data[p])
    			{
    				return p;
    			}
    		}
    		return -1;
    	}
    
    	// choose compare item
    	int f = len % 2;
    	int i = f == 0 ? len / 2 - 1: len / 2;
    
    	int v = data[i + b];
    	if (k < v)
    	{
    		len = f == 0? len / 2 - 1: len / 2;
    		return search(k, b, len);
    	}
    	else if (k > v)
    	{
    		len /= 2;
    		b = f == 0 ? b + len: b + len + 1;
    		return search(k, b, len);
    	}
    
    	return b + i;
    }
    
  728. Pingback: Newsletter KW 17 / 2010

  729. Pingback: links for 2010-04-27 « pabloidz

  730. Chris Pickett

    Failed… initial one worked, but I was literally splitting the array, which of course made me lose the one piece of information that was important. Re-wrote it to keep track of the first and last element to search between, and it works.

  731. Dallan Simper

    Probably missed the deadline for counting in the survey.

    int location = RecBSearch(inputlist, value, 0, (inputlist.Length – 1));
    private static int RecBSearch(int[] inputlist, int value, int start, int end)
    {
    if (start == end && value == inputlist[start])
    return start;
    else if (start == end)
    return -1;

    if (value inputlist[end])
    return -1;

    int middle = (start + end) / 2;

    if (value == inputlist[middle])
    return middle;
    else if (value < inputlist[middle])
    return RecBSearch(inputlist, value, start, middle);
    else
    return RecBSearch(inputlist, value, (middle + 1), end);
    }

    private static int BSearch(int[] inputlist, int value)
    {
    return -1;
    }

    [\source]

  732. First version of the code was :

    let binsearch arr x =
        let rec aux a b =
            assert (b > a) ;
            assert (arr.(a) <= x) ;
            if arr.(a) = x then a
            else if b = a+1 then raise Not_found
            else let h = (a+b)/2 in
            if arr.(h) <= x then aux h b
            else aux a h
        in
        let a, b = 0, Array.length arr in
        if b = 0 then raise Not_found else aux a b
    

    Which crashed on the second test of the file available at http://www.mac-guyver.com/switham/2010/04/Binary_Search/.

    Of course this assertion asserts that the searched ‘x’ belongs to the array :
    assert (arr.(a) <= x) ;

    Once removed this assert clause, pass all 4k tests.

    So the morale of the story is : don't test too much :-D

  733. C#, success. Took 40 minutes, because initially I was doing a lot of testing for specific edge cases like “hi and lo are neighboring” or “they occupy the same index”, but then I remembered that you use mid-1 or mid+1 to exclude previously tested values from your hi/lo boundaries. Some extra desk checking (on paper) and I found I didn’t need the code for the edge cases. The default logic handles them fine.

    Also, I downloaded Steve Witham’s 4096 tests, and wrote some regexes to parse the text file and run the tests. All 4096 passed, first try. =)

    I have not tested this will nullable types, and how it would deal with nulls, both in the list being searched, and as an argument to search for. I imagine I’d just get a NullReferenceException when calling .CompareTo().

    		private static int binsearch<T>(IEnumerable<T> source, T searchValue, int lo, int hi)
    			where T : IComparable<T>
    		{
    			// Integer division will round down.  So with 4 elements (either "no middle" element or "2 middle" elements, depending on your view),
    			// the "first middle" element will be chosen.
    			int mid = lo + ((hi - lo) / 2);
    
    			// edge case: empty array (causes lo=0, hi=-1 on first call),
    			// or, the lo and hi boundaries have crossed.  Search value not found.
    			if (lo > hi)
    				return -1;
    
    			if (source.ElementAt(mid).CompareTo(searchValue) < 0)
    				// "mid" value is too low, bring the "lo" boundary up to the current "mid" index (+1, since you tested that particular value already.  It's the current "mid")
    				return binsearch(source, searchValue, mid+1, hi);
    			if (source.ElementAt(mid).CompareTo(searchValue) > 0)
    				// "mid" value is too high, bring the "hi" boundary down to the current "mid" index (-1, since you tested that particular value already.  It's the current "mid")
    				return binsearch(source, searchValue, lo, mid-1);
    			// Base case: value found
    			return mid;
    		}
    
  734. Jimmy Selgen Nielsen

    Sad to say i’m not in the 10%.
    Took 5 mins to write, 7 mins to debug though.

    def bsearch(value,arr)
      s = 0
      e = arr.length
     
      while(s < e-1)
        m = (e-s)/2 + s
        if(value > arr[m])
          s = m
        elsif(value < arr[m])
          e = m
        elsif(arr[m] == value)
          return m
        end
      end
    end
    
  735. Pingback: Binärsortering « Åke i exil

  736. Pingback: Room for Sk3pticism » Recursive binary search in C

  737. def search(list, sought):
      mid = len(list)/2
    
      if len(list) == 0: return None
                                                                                                          
      if sought < list[mid]: return search(list[:mid], sought)
      elif sought > list[mid]: return search(list[mid+1:], sought)
      elif sought == list[mid]: return mid
    
  738. #!/usr/bin/env python2.5
    # Testing out this :P
    # https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    
    def log(msg, verbose):
        if verbose:
            print msg
    
    
    def bsearch(l, i, offset=0, verbose=True, step=1):
        """
        bsearch finds the element i in the list l doing a binary search
        If verbose is set to True will the algorithm will print out each iteration,
        the parameters for each and the number of steps
        """
        pos = len(l)/2
        log('Searching for %s in %s' % (i, l), verbose)
    
        if not l:
            return 'Value not found in array'
            
        el = l[pos]
        log('STEP: %s\n' % step, verbose)
        if i == el:
            return 'Found at pos %s' % (offset + pos)
        else:
            log('Not found', verbose)
            if i > el:
                # i is in the 2nd half
                log('It is on second half', verbose)
                new_l = l[pos+1:] # Does not contain the [pos] element
                offset = pos + 1
            else:
                # i is in the first half
                log('It is on first half', verbose)
                new_l = l[:pos]
                offset = offset
    
            return bsearch(new_l, i, offset=offset, verbose=verbose, step=step+1)
    
    
    test_array1 = [2, 4, 6, 8, 10]
    test_array2 = [1, 4, 6, 8, 10, 12, 13, 17, 21, 34, 101, 567]
    
    if __name__ == '__main__':
        assert bsearch(test_array1, 2) == 'Found at pos 0'
        assert bsearch(test_array1, 4) == 'Found at pos 1'
        assert bsearch(test_array1, 6) == 'Found at pos 2'
        assert bsearch(test_array1, 8) == 'Found at pos 3'
        assert bsearch(test_array1, 9) == 'Value not found in array'
        assert bsearch(test_array1, 5) == 'Value not found in array'
        assert bsearch(test_array1, 1) == 'Value not found in array'
    
        assert bsearch(test_array2, 6) == 'Found at pos 2'
        assert bsearch(test_array2, 12) == 'Found at pos 5'
        assert bsearch(test_array2, 13) == 'Found at pos 6'
        #assert bsearch(test_array2, 567) == 'Found at pos 11'
        assert bsearch(test_array2, 9) == 'Value not found in array'
        assert bsearch(test_array2, 105) == 'Value not found in array'
        assert bsearch(test_array2, 0) == 'Value not found in array'
    
        print "Congrats Jj :)"
    

    I commented the 567 search because that one failed :(. If it didn’t occur to me to write that test I wouldn’t have noticed and think I got it :P

  739. I tested my code somewhere above with border cases and all permutations of array elements up to array size 9, and from looking at other comments I would say that my code is correct. I just post this afterwards because my initial comment was somewhat truncated by the human editing machine ;-)

    Cheers
    Sorokan

  740. Pingback: Writing correct code, part 3: preconditions and postconditions (binary search part 4c) « The Reinvigorated Programmer

  741. Success. Iterative approach, although I considered recursion thought that this would be simple enough without.

    Time required:
    – Read article
    – Got cup of coffee
    – Wrote code (python) using notepad
    – Hand checked (i.e., played computer in my mind. Does this count as “testing”? I think not).
    – Started python interactive session
    – pasted code from notepad
    – Tested
    – no errors found.
    – all boundary conditions succeeded
    – Total time 35 minutes.

    {source}
    def bsearch( s, v ) : # Binary search – sequence & value
    lo, hi = 0, len( s ) – 1; # lower & upper bounds
    result = -1; # start with “not found”
    while lo < hi : # while untested sequence remains
    m = ( lo + hi ) / 2; # midpoint
    if s[ m ] == v : # check for match
    result = m; # match found
    break; # we're done
    elif s[ m ] < v : # done with lower half?
    lo = m + 1; # yes – reset lower bounds
    else : # no
    hi = m – 1; # reset upper bounds
    if ( result == -1 ) and s[ lo ] == v :
    result = lo; # special case
    return result # return index of matching value, or -1
    {/source}

  742. Tried it, inverted the gt and lt signs as usual, also doesn’t seem to work with arrays where one of the value is undefined… so not quite production code, but nice little challenge for half an hour.

    function binSearch(searchArray, searchValue, base)
    {
      if (!base)   base=0;
    
      // No more array left... can't be found...
      if (searchArray.length<1) return false;
      // Must be searching for a number...
      if (typeof searchValue!=='number') return false;
    
      var index = Math.floor(searchArray.length / 2);  // 4/2 = 2, 5/2 = 3
      if (searchArray[index]===searchValue) { // We found it!
        return base+index;
      }
      else if (searchArray[index]>searchValue) { // Go lower...
        return binSearch(searchArray.slice(0,index), searchValue, base);
      }
      else if (searchArray[index]<searchValue) { // Go higher...
        return binSearch(searchArray.slice(index+1), searchValue, base+index+1);
      }
    }
    
  743. Failed. Minor syntax error (function invocation with parentheses in PowerShell doesn’t quite work; I regularly forget) but most importantly an endless loop in case the elemtn wasn’t found. It just spits out “False” over and over again.

    function binary_search([array]$a, $v) {
        function bin_search([array]$a, [int]$start, [int]$end, $v) {
            if ($start -gt $end) {
                $false
                return  # this was missing, sadly.
            }
            
            [int]$midpoint = [Math]::Truncate(($start + $end) / 2)
            
            if ($a[$midpoint] -eq $v) {
                $true
            } elseif ($a[$midpoint] -lt $v) {
                bin_search $a ($midpoint+1) $end $v 
            } else {
                bin_search $a $start ($midpoint-1) $v
            }
        }
        
        bin_search $a 0 $a.Length $v
    }
    
  744. In the interests of good science: Wrote an iterative implementation in Python, but failed to account for the possibility of getting an empty sequence as input. So close!

  745. PHP solution using recursion:

    <?php
    function bSearch($ary, $val) {
    	if (empty($ary) || (count($ary) == 1 && $val != current($ary))) {
    		echo "$val not found\n";
    		return false;
    	}
    	
    	$mid = floor(count($ary) / 2);
    	if ($val == $ary[$mid]) {
    		echo "$val found\n";
    		return true;	// found
    	} else if ($val < $ary[$mid]) {
    		// search lo
    		bSearch(array_slice($ary, 0, $mid), $val);
    	} else {
    		// search hi
    		bSearch(array_slice($ary, $mid, count($ary)), $val);
    	}
    }
    ?>
    

    Took me 6 mins to whip up. Followed the conditions above and only tested when I was absolutely confident it would run correctly. Here’s what I used to test:

    <?php
    $array = array();
    $k = 10;
    for ($i = 0; $i < 50; $i++) {
    	$k = mt_rand(1,100);
    	if (!in_array($k, $array))
    		$array[] = $k;
    }
    sort($array);
    print_r($array);
    
    bSearch($array, current($array));
    bSearch($array, $array[5]);
    bSearch($array, 0);  // this will not be found
    bSearch($array, $array[10]);
    bSearch($array, $array[20]);
    bSearch($array, 412); // and neither will this
    bSearch($array, end($array));
    ?>
    

    Ran the first time, and test results were as expected. Is it entirely bug free? Dunno. Is it terribly memory efficient? Probably not. Does it work? I think so. Was it a success? You tell me.

  746. /***
    val is the sought after value, arr is the sorted array in which to look for the value
    binarySort should return the index of value val in the array arr, or -1 if val is not in arr
    ***/
    function binarySort(val, arr) {
    var mid = 0, // the middle index of the range, rounded down for arrays of even-numbered length
    min = 0, // the minimum index of the range in which to search for the val
    max = arr.length – 1; // the maximum index of the range in which to search for val

    while (min !== max) {
    mid = Math.floor((max – min + 1) / 2) + min;
    if (arr[mid] === val) {
    return val;
    } else if (arr[mid] < val) {
    min = mid;
    } else {
    max = mid;
    }
    }

    if (arr[min] === val) {
    return min;
    } else {
    return -1;
    }
    };

  747. Dang! Totally missed the zero-length array case. :-(


    /***
    val is the sought after value, arr is the sorted array in which to look for the value
    binarySort should return the index of value val in the array arr, or -1 if val is not in arr
    ***/
    function binarySort(val, arr) {
    var mid = 0, // the middle index of the range, rounded down for arrays of even-numbered length
    min = 0, // the minimum index of the range in which to search for the val
    max = arr.length - 1; // the maximum index of the range in which to search for val

    if (arr.length === 0) {
    return -1;
    }
    while (min !== max) {
    mid = Math.floor((max - min + 1) / 2) + min;
    if (arr[mid] === val) {
    return val;
    } else if (arr[mid] < val) {
    min = mid;
    } else {
    max = mid;
    }
    }
    if (arr[min] === val) {
    return min;
    } else {
    return -1;
    }
    };

  748. Ugh, one more stupid bug found, while testing. That should be the last bug, knock on wood.


    /***
    val is the sought after value, arr is the sorted array in which to look for the value
    binarySort should return the index of value val in the array arr, or -1 if val is not in arr
    ***/
    function binarySort(val, arr) {
    var mid = 0, // the middle index of the range
    min = 0, // the minimum index of the range
    max = arr.length - 1; // the maximum index of the range

    if (arr.length === 0) {
    return -1;
    }
    while (min !== max) {
    mid = Math.floor((max - min + 1) / 2) + min;
    if (arr[mid] === val) {
    return mid;
    } else if (arr[mid] < val) {
    min = mid;
    } else {
    max = mid;
    }
    }
    if (arr[min] === val) {
    return min;
    } else {
    return -1;
    }
    };

  749. @Eric Burnett: Yup! Thanks for pointing that out. In my bid to save an apparently unnecessarily recursive call, I managed to have that bug creep in …

  750. @Eric Burnett: On second thoughts, the code works perfectly for the single element cases! For instance:

    if the input is –

    int[] searchArr = {1};
    System.out.println(“index = ” + binarySearch(searchArr, 0, searchArr.length, 1));

    The program shows 0 as the output which is correct. Regarding the ‘single element arrays’ being generated as a result of recursion, I think if they contain the search key they will be handled in the previous recursive call (when the code checks for the key at ‘mid’ index). And if the search key is not present, the consequent recursive call will return -1.

    So the code handles the case :)!

    Let me know if there is any mistake in this argument.

    Thanks.

  751. It took 20-30 minutes and it worked like a charm the FIRST time! :)) I went the recursion way, I found that easier (and faster) to reason about than a loop algorithm. I loop thru all elements to make sure the end cases were covered and 2 not found cases, one less than the data and one greater than the data.

    #!/usr/bin/perl
    use strict;
    
    sub _bsearch($$$$)
    {
       my ($min, $max, $term, $aref) = @_;
       if ($min > $max)
       {
          return(undef);
       }
       else
       {
          my $mid = int(($min + $max) / 2);
          my $cmp = $term cmp $aref->[$mid];
    
          if (0 == $cmp)
          {
             return($mid);
          }
          elsif (-1 == $cmp)
          {
             return(_bsearch($min, $mid-1, $term, $aref));
          }
          elsif (1 == $cmp)
          {
             return(_bsearch($mid+1, $max, $term, $aref));
          }
          else
          {
             die("FATAL unreachable code");
          }
       }
    
    }
    
    
    sub bsearch($$)
    {
       my ($term, $aref) = @_;
       my $len = 0+@{$aref};
       return(_bsearch(0, $len-1, $term, $aref));
    }
    
    
    my @a = split //,"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    foreach my $term (@a)
    {
       print "Position == ", bsearch($term, \@a), "\n";
    }
    
    print "Position == ", bsearch('0', \@a), "\n";
    print "Position == ", bsearch('z', \@a), "\n";
    
  752. Given the comments I added 3 new bsearch calls and all give the expected results. I added 1 empty list call and 2 single element calls.

    print "Position == ", bsearch('z', []), "\n";
    print "Position == ", bsearch('z', ['a']), "\n";
    print "Position == ", bsearch('z', ['z']), "\n";
    

    So far I think this perl code passes.

  753. Success. Roughly five minutes of typing comments, another five of typing code.

    #!/usr/bin/env python
    # Implements a binary search as described on https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    # Assumes the list is strictly sorted and that the target is comparable with <, ==, > to every element in the list
    # Probably handles lists length MAXLISTINDEX+1 but precise typehandling is up to the Python interpreter.
    # This code is released to the public domain, with the exception of the quoted specification below
    def bsearch(sorted_list, target):
        # This specification is quoted from the above website, which in turn quoted Programming Pearls, 
        # and remains copyright the original authors.  It is believed that this inclusion constitutes fair use.
        #
        # Binary search solves the problem [of searching within a pre-sorted array] by
        # keeping track of a range within the array in which T [i.e. the sought value]
        # must be if it is anywhere in the array.  Initially, the range is the entire
        # array.  The range is shrunk by comparing its middle element to T and
        # discarding half the range.  The process continues until T is discovered in
        # the array, or until the range in which it must lie is known to be empty.  In
        # an N-element table, the search uses roughly log(2) N comparisons.
        # (end quoted material)
        
        # Pseudocode:
        # lower, upper as initial range    
        lower = 0
        upper = len(sorted_list) - 1
        
        # while the range is not empty:
        while lower <= upper:
            #   Find the midpoint between lower and upper
            midpoint = lower + (upper - lower) / 2
            #   Find the middle element of the list
            element = sorted_list[midpoint]
            
            #   Compare the middle element to the target
            
            if element == target:
                #     if equal, success (return index)
                return midpoint
            elif element > target:
                #     If the midpoint element is greater than the target: target must be in [lower, midpoint)
                upper = midpoint - 1
            else: # element > target
                #     If the midpoint element is less than the target: target must be in (midpoint, upper]
                lower = midpoint + 1
        # If the range is empty, return None for failure
        return None
    
    def main():
        # Test with: Empty list, list containing found elements at various indexes (and not-found elements), one-element list, longer list of no found elements
        for searchlist in ([], range(10,51,3), [15], range(100, 200, 1)):
            print [(repr(x), repr(searchlist[x])) for x in range(0, len(searchlist))]
            print '\n'.join([','.join((repr(x), str(bsearch(searchlist, x)))) for x in range(10, 50, 1)])
    
    
    
    if __name__ == '__main__':
        main()
    
  754. I think I succeeded. I think.

    def bsearch(n, arr):
        base = 0 
        end = len(arr)-1
        i = len(arr) / 2 
        while True:
            if arr[base+i] == n:
                return base+i
            if end - base == 0 or arr[base] > n or arr[end] < n:
                # Exhausted search space. Would have found match above.
                return -1
            if arr[base+i] > n:
                end = base+i - 1 
                i = (end - base) / 2 
            elif arr[base+i] < n:
                base = base+i + 1 
                i = (end - base) / 2 
    
  755. Only one (very simple, very stupid) bug! I meant to ‘test’ for it in my head, but I forgot before setting it in stone. I’ve fixed it below, but noted where the bug was.

    Note that this is in Google’s Go language, just for fun.

    type IntArray []int
    
    func (home IntArray) search(i int) int {
            if len(home) == 0 {
                    return -1
            }
    
            leftIndex := 0
            rightIndex := len(home) - 1
    
            for {
                    if home[leftIndex] > i {
                            return -1
                    }
                    if home[rightIndex] < i {
                            return -1
                    }
                    // I omitted the following originally!!
                    if home[rightIndex] == i {
                            return rightIndex
                    }
    
                    mid := (rightIndex + leftIndex) / 2
    
                    switch {
                            case home[mid] > i :
                                    rightIndex = mid
                            case home[mid] < i :
                                    if mid == leftIndex {
                                            return -1
                                    }
                                    leftIndex = mid
                            case home[mid] == i :
                                    return mid
                    }
            }
            return -1;
    }
    
  756. Jeff Hanke

    I failed to write it bugless, originally I had mid = (h-l)/2 :

    #!/usr/bin/python2.5
    def binsearch(array, value):
      l, h = 0, len(array)-1
      while (l <= h):
        mid = (h+l)/2
        test = cmp(array[mid], value)
        if test == 0:
          return mid
        elif test < 0:
          l = mid + 1
        else:
          h = mid - 1
      return -1
    
  757. Pingback: Git is a Harrier Jump Jet. And not in a good way « The Reinvigorated Programmer

  758. Success. I hope.

    def bsearch(value, array,offset=0):
    if len(array) == 0:
    return -1
    if len(array) == 1:
    return offset if array[0] == value else -1
    midpoint = len(array) / 2
    if value == array[midpoint]:
    return offset + midpoint
    if value < array[midpoint]:
    return bsearch(value, array[:midpoint],offset=offset)
    else:
    return bsearch(value, array[midpoint:],offset=midpoint+offset)

  759. Hi, I read your article over breakfast and I am intrigued!
    I can’t resist to jot down a (hopefully correct) solution,
    but I’ll have to leave for work soon, so I’ll test it this
    evening and report my mistakes… =)

    /*
      purpose
        find index of value in sorted (ascending) array of ints
      returns
        index if value is found, otherwise -1
      restrictions
        assumes 16 bit integers
        maximum array length is MAX_INT (i.e. 32767)
        if value exists multiple times, index may point to any of them
    */
    int binary_search(int *array,int length,int value) {
      int index;
      int delta;
      //-- fail on empty array
      if (!length) return -1;
      //-- test if first entry is solution
      if (*array==value) return 0;
      //-- fail for array with single element
      if (1==length) return -1;
      //-- calc initial index and delta
      //-- index=2^ceil(log2(length)-1)
      //-- delta=index/2
      index=length-1;
      index|=(index>>1);
      index|=(index>>2);
      index|=(index>>4);
      index|=(index>>8);
      index=(index>>1)+1;
      delta=index>>1;
      //-- binary search loop
      while (delta) {
        //-- return if found
        if (array[index]==value)
          return index;
        //--
        if (array[index]>value) {
          index-=delta;
          delta>>=1;
        } else {
          index+=delta;
          delta>>=1;
          //-- correct if index went past end of array
          while (index>=length) {
            //-- fail if out of delta
            if (!delta) return -1;
            //--
            index-=delta;
            delta>>=1;
          }
        }
      }
      //-- compare and return
      if (array[index]==value)
        return index;
      else
        return -1;
    }
    

    p.s.
    Now, after “jotting down” the solution I am (as usually ;-) surprised how long it took me – a bit over an hour. Much of the time went into mentally interpreting the code for corner case parameters.
    Now that I’ll be late work I surely hope it’s at least working properly! =)

    Kind regards and thanks for the fun blog entry,

    Frank Hirsch

  760. Frank,

    What is the purpose of this bit of the code?

      index=length-1;
      index|=(index>>1);
      index|=(index>>2);
      index|=(index>>4);
      index|=(index>>8);
      index=(index>>1)+1;
    
  761. Okay, I’m back and I ran a few test vectors. Everything seems to be fine! Yay! =)

    That funny bit of code in the calculation of 2^ceil(log2(length)-1) propagates any “1”-bit in the variable down until it hits the lsb.

    If we start with, say, x =
    10000000b and bit-or with x>>1, which is
    01000000b we get x =
    11000000b. Bit-or that with x>>2, which is
    00110000b we get x =
    11110000b. Bit-or that with x>>4, which is
    00001111b we get x =
    11111111b.

    If you take any number x, decrement it, downward propagate the “1” bits, and increment it again you get as result 2^ceil(log2(x)).
    I picked up that trick from a colleague a few years ago, when looking for a fast solution for just that problem, and it stuck with me because it is so neat.
    I’m doing a lot of work on embedded systems, where sometimes performance still counts. You might have guessed when I was assuming 16 bit integers. =)

    Kind regards,

    Frank Hirsch

  762. p.s.
    In case you wonder what the whole “index=2^ceil(log2(length)-1)” part is good for anyway, it’s just a bit of defensive programming. I might have implemented the algorithm slightly differently if I was allowed to test, but I thought “The heck, I’ll just inflate the thing to the next 2^n size and save myself a couple of doubts without even losing performance (apart from a small constant).”

  763. I just ran into coding after seeing the initial statement before reading about any experiment so I tested it a few times. I had two bugs (mid = arr[(first+last)/2]; instead of mid = (first+last)/2; and no ending condition for the recursion for no result) that I found after about three test runs.

    #include <cstdlib>
    #include <cstdio>
    #include <ctime>
    #define N 50
    
    int arr[N];
    
    void init()
    {
    	printf("Initializing array...\n\n");
    	for(int i = 0; i<N; i++)
    	{
    		arr[i] = rand()%N;
    		printf("  Array element %d initialized as %d.\n", i, arr[i]);
    	}
    	
    	printf("\nArray initialized as {");
    	
    	for(int i = 0; i<N-1; i++)
    		printf(" %d,", arr[i]);
    	printf(" %d }.\n\n", arr[N-1]);
    	
    	return;
    }
    
    void sort()
    {
    	printf("Sorting array...\n");
    	
    	bool swaps = true; int gap = N;
    	
    	while(!swaps || gap>1)
    	{
    		swaps = false;
    		gap = int(gap / 1.25);
    		if(gap<1)
    			gap = 1;
    		
    		for(int i = 0; i<N-gap; i++)
    		{
    			if(arr[i]>arr[i+gap])
    			{
    				swaps = true;
    				int tmp = arr[i];
    				arr[i] = arr[i+gap];
    				arr[i+gap] = tmp;
    				printf("Swapped element %d(%d) with %d(%d).\n", i, arr[i], i+gap, arr[i+gap]);
    			}
    		}
    	}
    	printf("\nArray is currently {");
    	
    	for(int i = 0; i<N-1; i++)
    		printf(" %d,", arr[i]);
    	printf(" %d }.\n\n", arr[N-1]);
    	
    	return;
    }
    
    int recurse(int first, int last, int a)
    {
    	printf("  Checking [%d, %d]...\n", first, last);
    	int mid = (first+last)/2;
    	
    	if(arr[mid] == a)
    	{
    		printf("Found %d at element %d.\n", a, mid+1);
    		return a;
    	}
    	if(first==last)
    		return 0;
    	if(arr[mid] > a)
    	{
    		return recurse(first, mid-1, a);
    	}
    	if(arr[mid] < a)
    	{
    		return recurse(mid+1, last, a);
    	}
    }
    
    int search(int a)
    {
    	printf("Beginning search for %d...\n", a);
    	return recurse(0, N, a);
    }
    
    int main(int argc, char* argv[])
    {
    	srand(time(NULL));
    	
    	printf("\n");
    	
    	init();
    	sort();
    	
    	search(23);
    	
    	return 0;
    }}
  764. protected static boolean binarySearch( int[] aSortedArray, int aSearchValue )
        {
            if( aSortedArray.length == 0)
            {
                return false;
            }
    
            int theStartingIndex = 0;
            int theEndingIndex = aSortedArray.length - 1;
    
            while ( theStartingIndex < theEndingIndex )
            {
                int theDifference = theEndingIndex - theStartingIndex;
                int theMidpoint = theStartingIndex + theDifference/2;
                int theValueAtMidpoint = aSortedArray[theMidpoint];
                if( theValueAtMidpoint == aSearchValue )
                {
                    return true;
                }
                else if( theValueAtMidpoint > aSearchValue )
                {
                    theEndingIndex = theMidpoint - 1;
                }
                else
                {
                    theStartingIndex = theMidpoint + 1;
                }
            }
    
            return aSortedArray[theStartingIndex] == aSearchValue;
        }
    
  765. Here’s a bsearch implementation I wrote to match the interface that C uses.

    Originally I wrote “if (num_objs > 0) return NULL”, which is obviously wrong. I didn’t notice until the first execution, so this counts as a failing entry.

    Aside from that line there are no other changes to what I wrote before testing.

    const void *bsearch(const void *key,
                        const void *base,
                        size_t num_objs,
                        size_t obj_size,
                        int (*compar) (const void *, const void *)) {
        for (;;) {
            if (num_objs < 1)
                return NULL;
            
            int pivot = num_objs / 2;
            const void *pivot_ptr = base + (pivot * obj_size);
            int res = compar(key, pivot_ptr);
            if (res == 0)
                return pivot_ptr;
            if (res < 0) {
                num_objs = pivot;
                continue;
            }
            base = pivot_ptr + obj_size;
            num_objs = num_objs - pivot - 1;
        }
    }
    
  766. Anton Pirogov

    #!/usr/bin/env ruby

    a1=[0,1,2,3,4,5,6,7,8,9]
    a2=[0,2,2,3,3,5,5,5,5,6,7,8,8,8,9]

    #NOTICE: NO TESTING!!! BEFORE FINISHING
    def get_index_of(array, element, starti, endi)
    starti = 0 if !starti
    endi = array.length-1 if !endi

    index = starti + (endi-starti)/2

    return index if array[index] == element
    return nil if starti==endi

    ind1 = get_index_of(array,element,starti,index)
    ind2 = get_index_of(array,element,index+1,endi)

    return ind1 if ind1
    return ind2 if ind2
    return nil
    end

    #testing
    puts get_index_of(a1,3,nil,nil).to_s
    puts get_index_of(a1,0,nil,nil).to_s
    puts get_index_of(a1,9,nil,nil).to_s
    puts get_index_of(a1,12,nil,nil).to_s
    puts get_index_of(a1,-2,nil,nil).to_s
    puts get_index_of(a2,4,nil,nil).to_s
    puts get_index_of(a2,5,nil,nil).to_s

    Results:
    3
    0
    9

    7

    -> WORKS :)))

  767. I did it in C in twenty minutes or so and… it failed for half (when numbers to be find were on “right” part):( Then I took a deeper look into my code, I debugged it “by mind” several time before saying “it is right” … and found the grand error: using size_t for indexes instead of signed int… so there was the condition that lead a while to loop forever or segmentation fault (r==-1 and l == 0 should stop the loop, but indeed r became 4billions or so, r > l for sure and it tried to access the array at that huge index)
    fixed size_t to int, it worked properly

  768. Did a quick and ugly version in lisp, looked it over very carefully, then tested and realized I forgot to add the starting offset back in when calculating the middle of the range.

    Oh well, I suppose I’m not very surprised at being one of the 90%

    (defun binary-search (item vector &key (start 0) end key
    (test-lessp #’string-lessp) (test-eql #’string-equal))
    (labels ((bsearch (start end)
    (if (= start end)
    nil
    (let* ((idx (floor (/ (- end start) 2)))
    (raw (aref vector idx))
    (val (if (null key) raw (funcall key raw))))
    (cond
    ((funcall test-eql val item)
    idx)
    ((funcall test-lessp val item)
    (bsearch idx end))
    (t
    (bsearch start idx)))))))
    (let ((idx (bsearch start (if (null end) (length vector) end))))
    (if (null idx)
    (values nil nil)
    (values (aref vector idx) idx)))))

  769. test of posting

    /*

    */
    type name(type1 arg1,  type2 arg2) {
      return (type)0;
    }
    /*
    

    */

  770. My recursive implementation failed, the looping implementation seems to work.

    http://www.astro.cornell.edu/~carcich/misc2/qbinsearch.c

  771. Yup, one bug in the recursive implementation, forgot about size of the members when using (void*) to pass the array base.

    http://www.astro.cornell.edu/~carcich/misc2/qbinsearch2.c

    Does getting the looping implementaion right put me in the 10%?

  772. Yes, Brian, so far as I am concerned, the point of this exercise is to get the algorithm right. So I think you can award yourself a pass (so long as you’ve tested all the relevant cases, of course — see the next article if you haven’t already done so). The moral, I guess, is that C is not a good language for sketching algorithms. You probably want one of executable-pseudocode languages (Ruby or Python) for this kind of exercise.

  773. Pingback: Another challenge: can you write a correct selection sort? « The Reinvigorated Programmer

  774. Might as well join the party!

    In Python, not tested:

    # returns the index of the target
    def bsearch(target, L):
        # interval where target might still be is L[left:right],
        # which includes L[left] but not L[right].
        left, right = 0, len(L)
        while left < right:
            mid = (left+right)/2      # integer division
            if L[mid] == target: return mid
            elif L[mid] > target: right = mid
            else: left = mid+1
        return None
    
  775. I only got it right on the third try. Oh well.

  776. Jim Sawyer

    Ok, I followed the rules.
    No looking at other entries, or implementations.
    Not yet tested — just desk checked.
    Written in Smalltalk.

    
    SequenceableCollection>>findFirst: key ifAbsent: a0block
        ^self findFirst: key or: [:na| a0block value]
    
    >>findFirstOrInsert: key
      ^self findFirst: key or: [:i| self at: i insert: key]
    
    >>findFirst: key or: a1block
    	"	pre)	self isSorted
    		O)	self size log: 2
    		A) 	value matching key, if present in receiver, such that
    				for all i in {1..index-1} self at: i < key
    				for all j in {index+1..size} key <= self at: j
    			or a1block evaluted with index for insertion of key into receiver if key is not present.
    	" 
    	| index |
    	index := self anyIndexOf: key or: [:i| ^a1block value: i].
    	^self at: (self index: index  orFirstDuplicate: key)
    
    >>findLast: key ifAbsent: a0block
        ^self findLast: key or: [:na| a0block value]
    
    >>findLastOrInsert: key
        ^self findLast: key or: [:index| self at: index insert: value]
    
    >>findLast: key or: a1block
    	"	pre)	self isSorted
    		O)	self size log: 2
    		A) 	index of key, if key is present in receiver, such that
    				for all i in {1..index-1} self at: i <= key
    				for all j in {index+1..size} key < self at: j
    			or a1block evaluted with index for insertion of key into receiver if key is not present.
    	"
    	| index |
    	index := self anyIndexOf: key or: [:i| ^a1block value: i].
    	^self at: (self index: index  orLastDuplicate: key)
    
    >>anyIndexOf: key or: a1block
    	"	pre)	self isSorted
    		O)	self size log: 2
    		A)	index of key, if key is present in receiver, or, if key is not present,
    			a1block evaluated with index for insertion into receiver.
    	"
    	^self anyIndexOf: key
    		fromUnchecked: 1 to: self size
    		or: a1block
    
    >>anyIndexOf: key from: start to: stop or: a1block
    	"	pre)	self isSorted
    		O)	stop - start log: 2
    		A)	index of key, if key is present in receiver between start and stop, or, if key is not present,
    			index for insertion into receiver between start and stop + 1.
    	"
    	(0 < start and: [stop between: start - 1 and: self size])
    		or:	[^self errorOutOfBounds].
    	^self anyIndexOf: key
    		fromUnchecked: start to: stop
    		or: a1block
    
    >>anyIndexOf: key fromUnchecked: start to: stop or: a1block
    	"	pre)	self isSorted and: [0 < start and: [start - 1 <= stop <= self size]]
    		I)	P and: [BB  not]
    		P)	0 < start <= L <= H <= stop <= self size and: [key <= (self at: H)] 
    		BB)	key = (self at: H) or: [ H - L == 0 ]
    		T)	stop - start
    		R)	T // 2
    		O)	T log: 2
    		A)	index of key, if key is present in receiver between start and stop, or, if key is not present,
    			a1block evaluated with index for insertion into receiver between start and stop + 1.
    	"
    	| low high |
    	" Key outside range? "
    	stop == start - 1 ifTrue: [^a1block value: start].
    	self wrt: stop is: key after: [:last| ^a1block value: last + 1].
    	" Key is in range. Establish I "
    	low := start. high := stop.
    	" I "
    	[ " BB? "
    	  key = (self at: high)
    		or:	[ high == low ]
    	] whileFalse:
    		[ self wrt: (high + low // 2)			" reduce T "
    			is: key
    			atOrBefore: [:mid| high := mid - 1]	" resestablish I "
    			orAfter: [:mid| low := mid + 1]		" resestablish I "
    		].
    	" BB "
    	^high == low
    		ifTrue: [a1block value: high]
    		ifFalse: [high]
    
    >>index: index orFirstDuplicate: key
    
    	key = (self at: index) ifFalse: [^0]. 
    	index - 1 to: 1 by: -1 do:
    		[:i| key = (self at: i) or: [^i + 1]].
    	^1
    
    >>index: index orLastDuplicate: key
    	| size |
    	key = (self at: index) ifFalse: [^0].
    	index + 1 to: (size := self size) do:
    		[:i| key = (self at: i) or: [^i - 1]].
    	^size
    
    >>wrt: index is: aValue after: after1block
    
    	^self wrt: index
    		is: aValue
    		atOrBefore: [:na| nil]
    		orAfter: after1block
    
    >>wrt: index is: aValue atOrBefore: atOrBefore1block orAfter: after1block
    
    	^aValue <= (self at: index)
    		ifTrue: [atOrBefore1block value: index]
    		ifFalse: [after1block value: index]
    
    SortedCollection>>wrt: index is: aValue atOrBefore: atOrBefore1block orAfter: after1block
    	| another |
    	(sortBlock value: aValue value: (another := self at: index))
    		ifTrue: [^atOrBefore1block value: index].
    	^aValue = another
    		ifTrue: [atOrBefore1block value: index]
    		ifFalse: [after1block value: index]
    
    
  777. When I read this in Bentley’s column, I thought that it begged for a declarative solution, but was too lazy to write one. You’ve spurred me on to do it:

    bin_search(L,I,X) :-
        halve_maybe_right_biased(L,Ll,M,Lr),
        (X < M ->
            bin_search(Ll,I,X)
        ;
            length(Ll,Len),
            (X > M ->
                bin_search(Lr,Ir,X),
                I is Ir + Len + 1
            ;
                I = Len
            )
        ).
    
    halve_maybe_right_biased(L,Ll,M,Lr) :-
        append(Ll,[M|Lr],L),
        match_with_right_buffer(Ll,Lr,[_]).
    
    match_with_right_buffer([],Lr,Buff) :- append(Lr,_,Buff).
    match_with_right_buffer([_|T1],[_|T2],Buff) :- match_with_right_buffer(T1,T2,Buf
    f).
    

    Aside from an argument-order error (for append), that’s my second attempt. The first attempt used a slightly different approach, which turned out (upon testing) to assume that the list-length was even:

    halve_maybe_left_biased(L,Ll,M,Lr) :-
    list_to_skel(L,Lskel),
    append(Ll,Lrskel,Lskel),
    list_to_skel(Ll,Lrskel),
    (
    append(Ll,[M|Lrskel],L), Lr = Lrskel
    ;
    append(Ll,Lrskel,L), Lrskel = [M|Lr]
    ).

    list_to_skel([],[]).
    list_to_skel([_|T1],[_|T2]) :- list_to_skel(T1,T2).

  778. I came late to the party, but, FWIW, this is what I wrote (in Python). AFAICT, it works, and worked on the first try:

    def search(value, array, low, high):
        if low > high:
            return None
        middle = (high + low)/2
        if array[middle] == value:
            return middle
        if value < middle:
            return search(value, array, low, middle-1)
        else:
            return search(value, array, middle+1, high)
    
    def binary_search(value, array):
        return search(value, array, 0, len(array)-1)
    
  779. I’m posting this blind, writing directly into notepad then pasting it in here. I think I’m writing the equivilant algo, but I immediately thought to just use a moving mid-point and step-size, and aligning everything to power-of-2 to keep things simple internally.

    If this isn’t the right algo, sorry! It keeps the code much, much simpler as far as I can see.

    {source}
    int binarysearch(unsigned int **array, unsigned int goal, unsigned int items) {
    unsigned int step;
    unsigned int point;

    if (items == 0) {
    return 0;
    }

    /* First, calculate the initial power-of-2 step-size. */
    step = items >> 1;
    step |= (step >> 1);
    step |= (step >> 2);
    step |= (step >> 4);
    step |= (step >> 8);
    step |= (step >> 16);
    step |= (step >> 32);
    step++;

    point = step;

    for (;;) {
    if (array[point] == goal) {
    return 1;
    }
    step >>= 1;
    if (step > 1) {
    return 0;
    }
    if (array[point] > goal) {
    point -= step;
    } else {
    point += step;
    if (point >= items) {
    point = items – 1;
    }
    }
    }
    }
    {/source}

  780. If Fail? How so. Otherwise, you’re just adding noise to things. Hell, you didn’t even direct this post at anyone specific in the thread so far.

  781. WolfWings, my understanding is that 45% was reporting that he had attempted the binary-search challenge and failed — not that he was accusing anyone else of failure. And so it’s not just noise, it’s a data point.

  782. I haven’t run my code yet, so I don’t know whether it passes (or even whether it is syntactically correct). But here it is, in Ruby:

    a = [1,2,3]
    t = 2

    range_min = 0
    range_max = a.length – 1

    top = range_max
    bot = range_min
    mid = (top + bot)/2

    if a[0] == t then
    return 0
    elsif a[a.length – 1] == t then
    a.length – 1
    elsif t a[a.length – 1]
    nil
    else
    while bot != top
    if t == a[mid] then
    top = bot = mid
    elsif t < a[mid] then
    top = mid
    else
    bot = mid
    end
    end

    mid
    end

  783. OK, I doublechecked my code and my previous submission had bugs. I have not yet executed the code to test for correctness, so can I have a second submission? This is my final answer, promise.

    class BinarySearch < Array
    def binary_search(t)
    range_min = 0
    range_max = self.length – 1

    top = range_max
    bot = range_min
    mid = (top + bot)/2

    if self.empty? then
    nil
    elsif self[0] == t then
    0
    elsif self[self.length – 1] == t then
    self.length – 1
    elsif t self[self.length – 1]
    nil
    else
    while bot != top
    if t == self[mid] then
    top = bot = mid
    elsif t < self[mid] then
    top = mid
    else
    bot = mid + 1
    end
    mid = (top + bot)/2
    end

    if self[mid] == t then
    mid
    else
    nil
    end
    end
    end
    end

  784. Here’s a recursive solution without helper functions (probably makes it less efficient this way, but I think it’s clearer).

    {source}

    #!/usr/bin/python

    def bsearch(array, target):
    sta = array[0]
    mid = array[len(array) / 2]
    end = array[-1]

    if len(array) == 2:
    return (sta == target or end == target)
    if mid == target:
    return True
    if mid target:
    return bsearch(array[0:len(array)/2], target)

    print bsearch([1,2,4,5,8,9,12,15], 4)

    {/source}

  785. Whoops.

    
    #!/usr/bin/python
    
    def bsearch(array, target):
        sta = array[0]
        mid = array[len(array) / 2]
        end = array[-1]
    
        if len(array) == 2:
            return (sta == target or end == target)      
        if mid == target:
            return True
        if mid < target:
            return bsearch(array[len(array)/2:], target)
        if mid > target:
            return bsearch(array[0:len(array)/2], target)
            
    print bsearch([1,2,4,5,8,9,12,15], 4)
    
    
  786. Hi Zodiac,

    Your code fails for zero-length arrays, and for 1-sized arrays that don’t contain the target element (it actually enters an infinite loop when the sole array element is smaller than the target).

  787. How embarrassing, my code has a very silly bug: I test “if value < middle", instead of "if value < array[middle]" :P

  788. My first try:

    size_t find(int* arr,size_t len,int x)
        {
        size_t mid;
        mid = len/2;
        if(arr[mid] == x)  return mid;
        if(len == 1) return (size_t)-1;
        if(arr[mid] < x) return mid + find(arr+mid+1,len-mid-1,x);
        else return find(arr,mid,x);
        }
    

    (Fails due to in band signaling; ah, well.)

  789. boolean findNum( int[] arr, int num ){
    int lo = 0;
    int hi = arr.length – 1;
    if( arr.length = 0)
    return false;

    while(lo>1; //mid == (hi+lo)/2
    if( arr[mid] == num )
    return true;
    if( arr[mid] < num )
    lo = mid+1;
    else
    hi = mid-1;
    }
    return false;
    }

  790. // A small contribution from Israel

    Hi Mike,

    I liked this post so much that soon after I found out about it, I adapted it (with due credit) to my weekly programming column at an Israeli portal. My own attempt, in Object Pascal, was successul but the test program (that I wrote without testing as well) had a syntax error.

    Eleven of my readers took the challenge and bothered to report their results; Five of them got it right the first time. So overall, that’s 50% success, biases included.

    Thanks for this really interesting blog. You got yourself another follower :-)

  791. Thanks, Ido, interesting to hear about your results. One of the most humbling things about writing this blog is seeing the attention it occasionally gets in other languages — I’ve seen a Russian-language translation of one article, for example. The global village, indeed!

  792. Well, I took the challenge and passed, although I’m new to programming Java and I made a couple of rookie errors (like declaring a variable inside the loop and having Java get pissed at me for declaring more than once).

    Here’s the meat of it.


    do
    {
    myGuess = ((highIndex-lowIndex)/2)+lowIndex; // note this will round down

    System.out.println("Looking for your value " + myTarget + " at position " + myGuess +" ... value there is " + myVector[myGuess]);

    if (myTarget < myVector[myGuess])
    highIndex = myGuess - 1;
    else if (myVector[myGuess] < myTarget)
    lowIndex = myGuess + 1;
    } while (myVector[myGuess] != myTarget);

    System.out.println("Found your value " + myTarget + "at position " + myGuess +"... value there is " + myVector[myGuess]);

    I did not check for the case of value not found, although it would have been easy to break out of the loop if (highIndex == lowIndex) and I still have no match.

  793. Ooh, here’s the same thing implemented recursively, and also as a number guessing game!

    $ java RecursiveBinarySearch
    Think of a number between 1 and 100. I’ll guess it!
    82
    Is 50 your number?
    Nah, too low. I’ll try again.
    Is 75 your number?
    Nah, too low. I’ll try again.
    Is 88 your number?
    Nah, too high. I’ll try again.
    Is 81 your number?
    Nah, too low. I’ll try again.
    Is 84 your number?
    Nah, too high. I’ll try again.
    Is 82 your number?
    I guessed your number in 6 steps! GLEEEEE

    import java.util.*;
    public class RecursiveBinarySearch
    {
        public static void BSearch(int myTarget, int lowIndex, int highIndex, int myIterations, int[] myVector)
        {
    	int myGuess = ((highIndex-lowIndex)/2)+lowIndex; // note this will round down
    	System.out.println("Is " + myVector[myGuess] + " your number?");
    
    	if (myTarget < myVector[myGuess])
    	    { System.out.println("Nah, too high. I'll try again.");
    		highIndex = myGuess - 1;}
    	else if (myVector[myGuess] < myTarget)
    	    { System.out.println("Nah, too low. I'll try again.");
    		lowIndex = myGuess + 1; }
    	
    	if (myTarget != myVector[myGuess])
            {
    	    myIterations++;
    	    if (highIndex == lowIndex)
    		{ System.out.println("HEY! I said between 1 and 100. Do NOT screw with me like that.");
    		    return;
    		}
    	    else BSearch(myTarget,lowIndex,highIndex,myIterations,myVector);
    	}
    	else System.out.println("I guessed your number in  " + myIterations + " steps! GLEEEEE!");
    
    	return;
        }
    
        public static void main(String[] args)
        {
    	// Start with a vector of integers 1-100
    
    	int[] myVector = new int[100];
    
    	for (int j=0;j<=99;j++)
    	    myVector[j] = j+1;
    
    	// Prompt user for a number to find
    
    	int myTarget;
    
    	Scanner sc = new Scanner(System.in);
    	System.out.println("Think of a number between 1 and 100. I'll guess it!");
    	myTarget = sc.nextInt();
    
    	int lowIndex = 0;
    	int highIndex = (myVector.length) - 1;
    	int myIterations = 1;
    
    	BSearch(myTarget,lowIndex,highIndex,myIterations,myVector);
    
        }
    }
    
  794. Pingback: The Binary Search Challenge « Steve Daskam's Blog

  795. I was recently asked to perform this very task during a job interview in front of six people watching my every move on a whiteboard. Although I have coded this before for arrays and files (long ago), I found it surprisingly difficult to cobble together a coherent function (using iteration) in the 15 minutes alloted. Certain it still had significant flaws when “finished”.

    Which I think illustrates that some seemingly simple problems really aren’t (as some algorithms are easy to describe but tricky to code and vice versa). Typically because it is so easy to miss covering all the corner cases.

  796. Here’s mine (Python):

    def search(sorted_list, item):
        if len(sorted_list) == 0:
            return -1
        lower = 0
        upper = len(sorted_list) - 1
        while True:
            ii = int((lower + upper) / 2)
            if ii < lower or ii > upper:
                return -1
            elif sorted_list[ii] == item:
                return ii
            elif sorted_list[ii] > item: # item is to the left
                upper = ii - 1
            else: # item is to the right
                lower = ii + 1
    

    And it seems to be correct, according to my testing.

    When I was writing my code, I had these two comments at the top:

    #   0  1  2  3  4  5
    # [ a, b, c, d, e, f ]
    

    I used these two comments to test the code mentally. Does that count as cheating?

  797. David, nothing that you do mentally, including dry-running test-cases, is cheating. It’s getting the computer to do your testing for you that is cheating. See the followup article Testing is not a substitute for thinking (binary search part 3)

  798. Pingback: Why is Binary search elusive? « Pawn Island's Blog

  799. This is a report of failure.

    I was somewhat hopeful when I tested my 15 minute implementation of binary search, but much to my dismay, I encountered several errors. Because of off-by-one error the ending conditions were incorrect and the first element in array was effectively ignored.

    In retrospect, I should have planned my implementation properly instead of hastily diving into the code. And as a CS student, I should have known this.

  800. Didn’t make it :(
    I managed to create an infinite loop in a special case.

  801. Made this in Python:

    def binary_search(l, item, start_index=None, end_index=None):
        if start_index == None:
            start_index = 0
        if end_index == None:
            end_index = len(l)
        if end_index <= start_index:
            return None
        else:
            middle = (start_index + end_index) / 2
            if l[middle] == item: #meh
                return middle
            elif l[middle] > item:
                # item is in the first half
                return binary_search(l, item, start_index=start_index,
                                     end_index=middle)
            else:
                # item is in the second half
                return binary_search(l, item, start_index=(middle+1),
                                     end_index=end_index)
    
    
    def stupid_search(l, item):
        for i, listitem in enumerate(l):
            if listitem == item:
                return i
        else:
            return None
    
    
    def test(l, items):
        l.sort()
        for item in items:
            assert stupid_search(l, item) == binary_search(l, item), (l, item) 
        for item in l:
            assert stupid_search(l, item) == binary_search(l, item), (l, item) 
        print "all passed!"
    
    test([1, 2, 3, -10, "foo", "bar", None], ("bar", None, -50, 2.0))
    

    It passes the test; the only issue I thought of only while writing the test (before running them) is that it considers 2 and 2.0 as the same, so it will find 2.0 in a list that only contains integers. It could be fixed in the source, but I’m not sure that counts as a real bug.

  802. There was an error in the type signature, but the actual code was correct, as type signatures are optional in Haskell.

    Do I win?

  803. Success – that’s a scary exercise! Did it non-recursively to make it that bit harder :-) Should have posted the code *before* testing it, that would have been the brave thing.

    def binarysearch(array, val):
        lo = 0
        hi = len(array)
        while hi > lo:
            mid = (hi + lo)/2
            if array[mid] == val:
                return True
            elif array[mid] > val:
                hi = mid
            else:
                lo = mid+1
        return False
    
  804. Yay!

    def binary_search(lst, val):
        left = 0
        right = len(lst) - 1
        middle = (left + right) / 2 
        while left < right and lst[middle] <> val:
            if val < lst[middle]:
                right = middle - 1
            else:
                left = middle + 1
            middle = (left + right) / 2
        if lst and lst[middle] == val:
            return middle
    
  805. After reading through the submissions, I noticed I also forgot to catch the case that the search number is not in the array.

    Feel free to post tips.

    private void btnSearch_Click(object sender, EventArgs e)
    {
    int[] intArray = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
    int toSearch = int.Parse(txtSearch.Text);
    Search(toSearch, intArray);
    }

    private int Search(int toSearch, int[] intArray)
    {
    int[] receiveIntArray;
    int amount = intArray.Count();
    //21 / 2 = 10, position 10 in an array = 11
    //20 / 2 = 10, in this case you have to either pick 9 or 10.
    int middleIndex = amount / 2;
    int middleNumber = intArray[middleIndex];
    if (toSearch == middleNumber)
    return middleIndex;
    //dig deeper
    else
    {
    if (toSearch < middleNumber)
    {
    receiveIntArray = new int[middleIndex];
    Array.Copy(intArray, 0, receiveIntArray, 0, middleIndex);
    }
    else
    {
    receiveIntArray = new int[intArray.Count() – middleIndex];
    Array.Copy(intArray, middleIndex – 1, receiveIntArray, 0, intArray.Count() – middleIndex);
    }
    Search(toSearch, receiveIntArray);
    }
    return -1;
    }

  806. The one thing I ever wrote for the Apple ][ .

    Must have worked perfectly since no users ever got back about bugs…

    But in fact we never heard from any of them again.

  807. When I saw this I brought up a terminal in and came up with some python code in about 5 min. I then spent about 15 min writing test cases to see if I could break it. Tried every corner case I could think of, but it looks like I’m one of the 10%. Then again, being a CS major, algorithmic stuff is still pretty fresh in my mind.

  808. Well I’m only 15 so don’t hit me if I don’t totally get the concept of a binary search. I assume it just means divide an array into two (hint binary) and search each half of the array for a predetermined value. I used a template function because i felt the situation called for it. Also, I didn’t compile this. I did a quick jot-down in notepad++ because I’m not on my home computer.
    bool main(void);

    template
    T bSearch(const *T);

    bool main(void){
    unsigned int checkitem=2;
    unsigned int checkarray[5]={1,2,3,4,5};
    bSearch(checkarray,checkitem); //I don't feel like getting an error code :).
    return false;
    }

    template
    &T bSearch(const T const *tSrchArry,const T &tItem,bool getErr=true){
    //===============================================
    unsigned short iLowOffset, iMedOffset, iHighOffset;
    iHighOffset=sizeof(tSrchArry)-1;
    iMedOffset=iHighOffset/2;
    iLowOffset=iMedOffset;
    //================================================
    if(tSrchArry[iMedOffset]==tItem)
    return tSrchArry[iMedOffset];
    else{
    iMedOffset++;
    iLowOffset--;
    }
    for(unsigned short i=0;i<=iLowOffset;i++){
    if(tSrchArry[i]==tItem)
    return tSrchArry[i];
    }
    for(unsigned short j=iMedOffset;j<=iHighOffset;j++){
    if(tSrchArry[j]==tItem)
    return tSrchArry[j];
    }
    getErr=false;
    return tItem;
    }

  809. Sorry, the comment box messed up my indentation and it kinda jacked up some of the longer lines of code.

  810. It also messed up my template declerations…I guess because of the ‘s in “”

  811. David, enclose your code in {code}…{/code] tags, but using square brackets instead of curlies.

  812. // JavaScript
    function bsearch(haystack, needle) {
            var left = 0, right = haystack.length, mid, el;
            while (left < right) {
                    mid = Math.floor((left + right) / 2);
                    el = a[mid];
                    if (el == needle) return mid;
                    if (mid == left) break;
                    if (el < needle) left = mid;
                    else if (el > needle) right = mid;
            }
            return -1;              // -1 if not found
    };
    
  813. (well, there should be haystack[mid] instead of a[mid] on line 6; I just tested it now, seems to work fine given that change)

  814. Pingback: I guess I’m in with that 10% of programmers | Effluvium

  815. Mine had some “bugs” where the test output wasn’t pretty enough for me, other than that it’s fine. Maybe a bit underoptimized, but fine: http://ideone.com/jThqQ
    I looked at that overflow bug after finishing, that method of getting the midpoint bound seems more awkward to me, so I didn’t encounter it, oh well.
    I somewhat doubt the 90% statement, perhaps a more scientific approach than posting blog comments is needed. I may be spoiled by working in a nicer company with better developers, this would compound if other firms are left without these good developers and are the mess I imagine they are if the 90% bit is true.

  816. Briefly tested before submitted, but no bugs found (yet):

    def binsearch(needle, haystack, start=0, end=-1):
      """ end is exclusive, start inclusive
      """
      if end == -1:
        end = len(haystack)
      if end-start == 0:
        return -1
      m=start+((end-start)/2)      
      if haystack[m] == needle:
        return m
      if haystack[m] < needle:
        return binsearch(needle, haystack, m+1, end)
      else:
        return binsearch(needle, haystack, start, m)
    
    
    
  817. Thomas Wright

    Here is my solution in Perl 6:

    sub bsearch ($x, @xs) {
    	my $a = 0;
    	my $b = @xs - 1;
    	loop {		
    		my $i = ($a + $b) div 2;
    		if $x == @xs[$i] { return $i }
    		return -1 unless $b > $a;
    		if $x > @xs[$i] { $a = $i + 1 }
    		if $x < @xs[$i] { $b = $i - 1 }
    	}
    }
    

    My first (untested) attempt did fail as it infinitely looped when searching for the last element.

  818. Justin Calleja
    import java.util.Arrays;
    
    public class BSearch {
    
    	public static final int NOT_FOUND = -2;
    
    	public static int search(double[] sortedArr, double t) {
    		
    		if(sortedArr.length == 0) {
    			return NOT_FOUND;
    		} else {
    			return searchR(sortedArr, t, 0);
    		}
    		
    	}
    
    	private static int searchR(double[] sortedArr, double t, int offset) {
    
    		int half = sortedArr.length / 2;
    		double val = sortedArr[half];
    
    		if (t == val) {			
    			return half + offset;			
    		} else if (half == 0) {
    			return NOT_FOUND;			
    		} else if (t < val) {
    			double[] newArr = Arrays.copyOfRange(sortedArr, 0, half);			
    			return searchR(newArr, t, offset + 0);
    		} else {
    			double[] newArr = Arrays.copyOfRange(sortedArr, half,
    					sortedArr.length);			
    			return searchR(newArr, t, offset + half);			
    		}
    
    	}
    
    }
    
    // -- Testing --
    
    public class Main {
    
    	public static void main(String[] args) {
    		
    		double[] test = new double[] { 2.0, 4.4, 5.8, 9.0, 11.2, 65.3, 101.1, 200.0 };
    		
    		double d1 = 9.0;
    		double d2 = 2.0;
    		double d3 = 65.3;
    		double d4 = 99.9;
    		double d5 = 222.2;
    		double d6 = 200.0;
    		double d7 = 11.2;
    		
    		
    		int res1 = BSearch.search(test, d1);
    		int res2 = BSearch.search(test, d2);
    		int res3 = BSearch.search(test, d3);
    		int res4 = BSearch.search(test, d4);
    		int res5 = BSearch.search(test, d5);
    		int res6 = BSearch.search(test, d6);
    		int res7 = BSearch.search(test, d7);
    		
    		
    		System.out.println("search for " + d1 +  
    				((res1 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res1 + "\ntest[" + res1 + "] = " + test[res1] + "\n")));
    		
    		System.out.println("search for " + d2 +  
    				((res2 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res2 + "\ntest[" + res2 + "] = " + test[res2] + "\n")));
    		
    		System.out.println("search for " + d3 +  
    				((res3 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res3 + "\ntest[" + res3 + "] = " + test[res3] + "\n")));
    		
    		System.out.println("search for " + d4 +  
    				((res4 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res4 + "\ntest[" + res4 + "] = " + test[res4] + "\n")));
    		
    		System.out.println("search for " + d5 +  
    				((res5 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res5 + "\ntest[" + res5 + "] = " + test[res5] + "\n")));
    		
    		System.out.println("search for " + d6 +  
    				((res6 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res6 + "\ntest[" + res6 + "] = " + test[res6] + "\n")));
    		
    		System.out.println("search for " + d7 +  
    				((res7 == BSearch.NOT_FOUND) ? "... Not found.\n" 
    						: (", result = " + res7 + "\ntest[" + res7 + "] = " + test[res7] + "\n")));		
    
    	}
    
    }
    
    
  819. Justin Calleja

    oh btw, my first attempt failed because of not properly keeping track of the index. My second attempt failed, because I thought i could get rid of the first problem simply by ‘cutting’ down the array… but then i ended up losing the index (no planning just coding and debugging… yes i know bad… i also don’t have much time atm… half want to do this half don’t… it’s a nice distraction from exams tho :)

    Third time’s the charm. Took me just under an hour. Probably there are bugs in there.

  820. just happened upon this blog earlier today. I quite enjoy the Doctor Who reviews!
    here’s a naive ruby implementation, before I read the comments.

    class Array
    def bsearch(v)
    l=self.length
    low=0 #inclusive in array
    high=l #one past end of current section
    until (low==high)
    l=(high+low)/2
    case self[l] v
    when -1
    high=l
    when 0
    return l
    when 1
    low=l+1
    end
    end
    return nil
    end
    end

  821. Alas, a 10% programmer I am not. I got the test backwards, it should have been “v self[l]”

    here’s the correct code, this time in {source} brackets. (D’oh!)

    class Array
      def bsearch(v)
        l=self.length
        low=0 #inclusive in array
        high=l #one past end of current section
        until (low==high)
          l=(high+low)/2
          #puts "low=#{low} l=#{l} high=#{high}"
          case v <=> self[l]
            when -1
              high=l
            when 0
              return l
            when 1
              low=l+1
          end
        end
        return nil
      end
    end
    
  822. Benjamin Rapaport

    I think I made the 10%. Code below, in java.
    public class BinSearch {

    public int[] myInts;
    public int totalElements;
    public int maxElements = 1000;
    public static int notFound = -999;

    public BinSearch() {
    totalElements = 0;
    myInts = new int[maxElements];
    }

    public void insert(int i) {
    myInts[totalElements] = i;
    totalElements++;
    }

    public int findWithBinSearch(int val) {
    if (totalElements == 0)
    return notFound;
    else
    return findWithBinSearchHelper(val, 0, totalElements-1);
    }

    private int findWithBinSearchHelper( int val, int left, int right) {
    int middleIndex = (right + left) / 2;
    int middleElement = myInts[middleIndex];

    // val has been found, return the index
    if( val == middleElement )
    return middleIndex;
    if( right – left == 0 )
    return notFound;

    // if val is on left side of middleIndex
    else if (val < middleElement)
    return findWithBinSearchHelper(val, left, middleIndex);

    // if val us in right side of middleIndex
    return findWithBinSearchHelper(val, middleIndex+1, right);

    }

  823. didn’t managed it in the first try, it ran forever if the result wasn’t in list. Clean room testing on a piece of paper (paper is still king), and it’s working in all cases I’ve tried

  824. Pingback: 转:只有10%程序员能正确实现二分查找算法 | ☆零☆ || coderling

  825. Am I fired ? ;-)

    #!/usr/bin/ruby
    def bsearch(arr, value) # Returns nil or found index
    mid_pos = arr.length / 2
    return nil if mid_pos == 0
    return mid_pos if arr[mid_pos] == value
    return bsearch(arr[mid_pos..arr.length - 1) if arr[mid_pos] > value
    return bsearch(arr[0..mid_pos]) 
    end
    end
    
  826. Damn typo !! Here is a patch !

    line 6 : return bsearch(arr[mid_pos..arr.length - 1], value) if arr[mid_pos] > value
    line 7 : return bsearch(arr[0..mid_pos], value)
    
  827. you get partial credit for fixing it before the OCD among us got home from church and noticed it.

    still, I think you need to test for a match before the test for zero-length half-array (what if the initial array has one element which matches the “value” argument).

    Also, I think your code requires the array to be sorted highest-first, which is not the most common convention, but the problem statement doesn’t specify the sort order so I’d say you’re okay there.

  828. I did get this right (by which I mean it passes the tests I wrote) first time. Of course, it is possible that I’ve missed something.

    ;; binary-search :: vector number -> non-negative integer
    ;; if item is in the vector, return it's position in the vector,
    ;; otherwise return #f
    (define (binary-search vector item)
      (define (average x y)
        (/ (+ x y) 2))
      (define (search start end)
        (if (= end start)
            #f
            (let* ((middle (floor (average start end)))
                   (middle-val (vector-ref vector middle)))
              (cond ((< item middle-val)
                     (search start middle))
                    ((< middle-val item)
                     (search (+ 1 middle) end))
                    (else
                     middle)))))
      (search 0 (vector-length vector)))
    
    (binary-search (vector) 1) ; #f
    (binary-search (vector 0) 1) ; #f
    (binary-search (vector 1) 1) ; 0
    (binary-search (vector 1 2) 1) ; 0
    (binary-search (vector 1 2) 2) ; 1
    (binary-search (vector 1 2) 0) ; #f
    (binary-search (vector 1 3 5 7 9 11) 9) ; 4
    (binary-search (vector 1 3 5 7 9 11) 11) ; 5
    (binary-search (vector 1 3 5 7 9 11) 0) ; #f
    (binary-search (vector 1 3 5 7 9 11) 1) ; 0
    (binary-search (vector 1 3 5 7 9 11) 88) ; #f 
  829. Pingback: Wojons » What does your broswer do with that CSS

  830. It seems like the point of this whole exercise is to demonstrate the importance of testing. The issue is not the algorithm. The issue is that *surprise!* code rarely performs perfectly the first time. Thus the growth of test-driven development.

    We have come a long way (I thought) since the era of the macho code-cowboy who writes a program with ‘ cat >’ .

  831. Here’s a code written in javascript. It may have bugs, it works (surprisingly to me) and I did follow the rules (I tested only once and I didn’t modify it after testing). Here’s the code.

    function findIndexOf(lst, x){
    for(
    var range = [0,lst.length-1], cursor = Math.floor(lst.length/2);
    x >= lst[range[0]] && x lst[cursor]){
    range = [cursor+1, range[1]];
    cursor = range[0] + Math.floor((range[1] – range[0])/2);
    }
    else if(x < lst[cursor]){
    range = [range[0], cursor-1];
    cursor = range[0] + Math.floor((1 + range[1] – range[0])/2);
    }
    }
    return -1;
    }
    //test it
    var list = [1,2,45,256,304,780,804,814,1456,2045];
    findIndexOf(list,780);
    findIndexOf(list,500);
    findIndexOf(list,2045);

  832. Oh, I don’t want to get into wordpress format wars, it’s so sad wordpress doesn’t have anything for it, but 4th line should be changed to x gte lst[range[0]] && x lte lst[cursor]semicolon rightparen left curly bracket

  833. : tempest; date; cat > binsearch.c; cc -o binsearch binsearch.c; ./binsearch; date
    Tue Dec 13 20:28:20 EST 2011
    /*
     * Binary search in C.
     */
    
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int 
    binsearch(int a[], size_t alen, int value)
    {
    	size_t l, m, r;
    
    	l = 0;
    	r = alen - 1; 
    	while (l <= r) {
    		m = l + (r - l)/2;  
    		if (a[m] == value) {
    			return m;
    		} else if (value < a[m]) {
    			r = m - 1;
    		} else {
    			l = m + 1;
    		}
    	}
    
    	return -1;
    }
    
    int
    main(void)
    {
    	int k, a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    	for (k = 0; k < 10; k++) {
    		int r = binsearch(a, 10, k);
    		if (r != k) {
    			fprintf(stderr, "%d != k\n", r, k);
    		}
    	}
    
    	return(EXIT_SUCCESS);
    }
    Tue Dec 13 20:32:21 EST 2011
    : tempest; 
    
  834. I am posting this (Java) version before testing.
    I am cheating slightly because (1) I have implmented binary search before and (2) I am editing in Eclipse with its syntax checking enabled.
    So it will be slightly embarassing if it doesn’t work…

    public class MyBinarySearch {
    
        public static void main(String[] args) {
    
            testSearch(new int[] {});
            testSearch(new int[] {1});
            testSearch(new int[] {1,100});
            testSearch(new int[] {20,40,60});
            testSearch(new int[] {9,99,99,99});
            testSearch(new int[] {9,9,9,9,99});
            testSearch(new int[] {0,20,40,40,60,80});
            testSearch(new int[] {9,10,11,39,40,41,69,70,71});
        }
    
        public static void testSearch(int[] array) {
            for (int key: array) {
                testSearchWithKey(array, key, true);
            }
            testSearchWithKey(array, -1000000, false);
            testSearchWithKey(array, 42, false);
            testSearchWithKey(array, 1000000, false);
        }
    
        private static void testSearchWithKey(int[] array, int key, boolean expectToFind) {
            int index = search(array, 0, array.length, key);
            if (index >= 0) {
                assert(expectToFind);
                assert(array[index] == key);
            } else {
                assert(! expectToFind);
                int insertionPoint = ~index;
                assert (insertionPoint <= 0 || array[insertionPoint-1] < key);
                assert (array.length <= insertionPoint || key < array[insertionPoint]);
            }
        }
    
        /**
         * @param array Array to search
         * @param lower Minimum index (inclusive)
         * @param upper Maximim index (exclusive)
         * @param key Value to search for
         * @return non-negative i if array[i] == key<br/>
         *         Otherwise ~i (ones complement of i) if an insertion sort would place key at position i
         *         (this is what {@link Arrays.binarySearch} does) 
         */
        public static int search(int[] array, int lower, int upper, int key) {
            while (lower < upper) {
                int mid = (lower + upper) / 2; // Note: NOT guarded against overflow
                if (key < array[mid]) {
                    upper = mid;
                } else if (array[mid] < key) {
                    lower = mid + 1;
                } else {
                    assert (array[mid] == key);
                    // Always return the leftmost occurrence
                    while (mid > 0 && array[mid-1] == key) --mid;
                    return mid;
                }
            }
            assert (lower == upper);
            return ~lower;
        }
    
    }
    
  835. Well it passes the test cases, but it fails as a binary search because it degrades from O(log N) to O(N) when there are duplicate keys (IIRC binary search must be O(log N) in worst case.) Also there are errors in the javadoc comment.

  836. STOP PUTTING RANDOM SUSHI ON THIS!!! PUT SALMON ISTEAD!!!

  837. Mark H Weaver
    ;; R5RS Scheme
    ;; (bsearch x v lo hi) searches for the number 'x'
    ;; in the sorted vector of numbers 'v', in the range of
    ;; indices between 'lo' (inclusive) and 'hi' (exclusive).
    ;; Returns the index, or #f if 'x' is not found.
    (define (bsearch x v lo hi)
      (and (< lo hi)
           (let* ((i (quotient (+ lo hi) 2))
                  (xi (vector-ref v i)))
             (cond ((< xi x) (bsearch x v (+ i 1) hi))
                   ((> xi x) (bsearch x v lo i))
                   ((= xi x) i)))))
    
  838. I just thought about this today and wanted to see the thread again, when I realized that there was a bug in my solution: it didn’t properly handle arrays of size 0. Also, the fprintf() call’s arguments didn’t match the format string. Finally, it didn’t test any failure cases, which is arguably a bug.

    Here’s a corrected version:

    : hurricane; cat bsearch.c
    /*
     * Binary search in C.
     */
    
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int 
    binsearch(int a[], size_t alen, int value)
    {
    	int l, m, r;
    
    	if (alen < 1)
    		return -1;
    	l = 0;
    	r = alen - 1; 
    	while (l <= r) {
    		m = l + (r - l)/2;  
    		if (a[m] == value)
    			return m;
    		else if (value < a[m])
    			r = m - 1;
    		else
    			l = m + 1;
    	}
    
    	return -1;
    }
    
    void
    test(int a[], size_t alen, int value, int expected)
    {
    	int r = binsearch(a, alen, value);
    	if (r != expected)
    		fprintf(stderr, "value %d at index %d not expected %d\n",
    		    value, r, expected);
    }
    
    int
    main(void)
    {
    	int k, a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    	size_t alen = sizeof(a) / sizeof(a[0]);
    
    	for (k = 0; k < 10; k++)
    		test(a, alen, k, k);
    	test(a, alen, -1, -1);
    	test(a, alen, 10, -1);
    	test(a, alen, 100, -1);
    	test(a, 0, -1, -1);
    	test(a, 0, 0, -1);
    	test(a, 0, 1, -1);
    	test(a, 0, 9, -1);
    	test(a, 0, 100, -1);
    
    	return(EXIT_SUCCESS);
    }
    : hurricane; make bsearch
    cc     bsearch.c   -o bsearch
    : hurricane; ./bsearch
    : hurricane; 
    
  839. (Note that this is only correct for arrays where alen fits into an ‘int’. Also, not iterating over alen in the for() loop could be considered poor form, but was deliberate.)

  840. Ack. There were other problems with it, too. Here’s the final bsearch() function:

    int 
    binsearch(int a[], size_t alen, int value)
    {
    	int l, m, r;
    
    	l = 0;
    	r = alen - 1; 
    	while (l <= r) {
    		m = l + (r - l)/2;  
    		if (a[m] == value)
    			return m;
    		else if (value < a[m])
    			r = m - 1;
    		else
    			l = m + 1;
    	}
    
    	return -1;
    }
    

    The problems with the original version included:

    1. Not supporting zero-length arrays (because l, r, and m were ‘size_t’, which is unsigned, r = alen – 1; would overflow to some large positive value, certainly out of the range of my test array, causing a core dump).
    2. Not supporting finding values smaller than the smallest element in a (again, due to l and r being unsigned, we’d end up with ‘r’ being set to -1 as the routine attempted to search off the left side of a. That would overflow to something outside the range of valid indices for a. Note that the inverse would not necessarily happen).
    3. The format statement didn’t match the arguments to fprintf().
    4. Tests didn’t cover elements outside of the array, or arrays of zero length (by testing those cases, I would have spotted [1] and [2] immediately).
    5. Again, one could argue that all the manifest constants in main() are esthetically unpleasing.

    There are, perhaps, several morals of this story: testing is great, but only if you test the things that actually matter (e.g., the test code was broken; this blog post and its follow-ups make that point repeatedly). Second, the code we think is right is often not, and if our tests are intended to *confirm* that belief, we are shooting ourselves in the foot. Instead, we should seek to write tests that *disprove* our belief in what we’ve created: that’s when we’ll uncover problems. Finally, perhaps the biggest lesson is: don’t try and write something in 4 minutes from memory just to show off; one is likely to embarrass oneself. :-)

  841. Thanks for this sequence of posts, Dan: I’m impressed by your honesty, and particularly like the morals you drew. The best, I think, is this one: “we should seek to write tests that *disprove* our belief in what we’ve created: that’s when we’ll uncover problems”.

  842. It certainly makes the case for TDD.

  843. Thanks, Mike! I guess I should note that the caveat about ‘alen’ fitting into the positive range of an ‘int’ still applies….

    Ignatius: I don’t know about that. In my original post, I had test cases; I just wasn’t testing the right things. TDD wouldn’t necessarily have helped that.

    Something that kind of struck me about this exercise was the extent to which the problems showed up in details. The basic structure of a (correct) binary search was there in the first version (indeed, the final version is the same, just with ‘size_t’ changed to ‘int’), I just didn’t think through the consequences of using unsigned variables for the indices. If I had written a more comprehensive sets of tests, it may have exposed the problem faster, but I’d still have to reason through it to figure out why the tests were failing and how to fix the program. Whether I’d written the tests before or after the code is unlikely to have mattered.

  844. I agree, Dan, that TDD would not likely have helped you much here. If you’ve not already seen it, you may enjoy part 3 of this binary-search series, Testing is not a substitute for thinking, in which I expound my own skepticism about TDD.

  845. I am pretty sure TDD would have helped you here, for the following reason: TDD is not simply writing tests first. It is a rigorous process which enforces clear thinking.

    Mike is absolutely correct that testing is not a substitute for thinking. You still need to have an understanding of how to solve the problem when using TDD. What TDD does is force you to realize what assumptions you are making. It is not unlike the scientific method: make observations, modify theories, make more observations, modify theories a little more.

    In this case, one of the first tests you would have written (probably the first) would have been the zero-length test. Now code the simplest solution which could possibly work. Then the one-length test. (Code again.) Then maybe the duplicate value test. Then probably reverse-ordered test. Etc. Between each of these steps, refactor to remove duplication and simplify the code.

    There’s a misunderstanding of what TDD provides. I’m not interested in getting into religious wars, but I would like for one camp to understand the other enough to not misrepresent it.

  846. OK, Chris, that makes sense. To my surprise, one of the most common mistakes in posted solutions has been inability to handle the degenerate (zero elements) case — and, yes, a well-written suit of tests would certainly have prevented that.

    So insofar as TDD means “thinking clearly about exactly what your program is trying to achieve”, I am all for it. I just don’t see that you have to do TDD in order to think clearly.

    BTW., you may be interesting in What does it take to test a sorting routine?.

  847. Maybe Chris, but then arguably *any* process that enforces rigorous thinking and reasoning about the problem would have helped. The, “Let me type this in from memory as fast as I possibly can…” thing did not. I agree with Mike’s point (and btw, Mike, I’m enjoying your blog).

    As I think about this, it would be interesting to do a post about how one would think about this in a TDD sense: “Okay, I want to implement binary search; what’s the most basic case? Searching in an empty array; that’ll always fail. Okay, write a test that calls the search routine with a zero-length array. Now, write a search routine that always returns -1.”

    void
    test_zero()
    {
            int a[] = {};
            size_t alen = 0;
    
            expect(bsearch(a, alen, 5), -1);
    }
    
    int
    bsearch(int a[], size_t alen, int value)
    {
            return -1;
    }
    

    What next? Write a test that looks for an element in an array of length 1; the element is either there or not.

    void
    test_one()
    {
            int a[] = { 0 };
            size_t alen = 1;
    
            expect(bsearch(a, alen, 0), 0);
            expect(bsearch(a, alen, -1), -1);
            expect(bsearch(a, alen, 1), -1);
    }
    
    int
    bsearch(int a[], size_t alen, int value)
    {
            if (alen != 1)
                    return -1;
            if (a[0] != value)
                    return -1;
            return 0;
    }
    

    I have some duplication in the return values, let me refactor:

    int
    bsearch(int a[], size_t alen, int value)
    {
            if (alen != 1 || a[0] != value)
                    return -1;
            return 0;
    }
    

    …and so on. Eventually, I’ll converge on some kind of binary search, maybe by first implementing a naive linear search (just to get a test to pass) and then refactoring into the final binary search. But is this really necessary? I claim it is not.

    Consider that in my original post, the binary search algorithm was correct, but the implementation was broken because of that pesky issue of signed versus unsigned indices. Indeed, to make it “correct”, all I really had to do was change ‘size_t’ to ‘int’ (with the caveat that the size of the array must be representable in the positive range of an ‘int’). By adding a few more test cases, I’d have seen that right away (and those test cases were kind of obvious: check for elements outside of the array on both sides). TDD may have helped, but wasn’t necessary. Just thinking about what I was doing a little more rigorously would likely have been sufficient. Indeed, to go back to Bentley’s original article from Programming Pearls, this what he stresses: being rigorous in our treatment of invariants, thinking through what each iteration of the loop does (and how it reduces the search range), etc. Here’s Bentley’s code:

    L := 1; U := N
    loop
        { MustBe(L, U) }
        if (L > U) then
            P := 0; break
        M := (L+U) div 2
        case
            X[M] < T:  L := M+1
            X[M] = T:  P := M; break
            X[M] > T:  U := M-1
    

    And then he writes:

    “It’s a short program: ten lines of code and one invariant assertion. The basic techniques of program verification – stating the invariant precisely and keeping an eye towards maintaining the invariant as we wrote each line of code – helped us greatly as we converted the algorithm sketch into pseudocode. This process gives us some confidence in the program, but we are by not means certain of its correctness.”

    No TDD here, but a definite nod to formal methods. Sadly, no consideration of overflow, either, but I don’t think that was the (intellectual) point. It’s easy enough to add an invariant that takes into account index overflow. Personally, I find Bentley’s method more intellectually satisfying than TDD’ing one’s way to an emergent solution. Carefully considering the program invariants and noting how each line of code contributes to the algorithm while preserving those invariants ensures a deep understanding of the problem domain, while the TDD approach doesn’t really do that. When reading about TDD, I often get this slightly uneasy feeling that the authors are kind of saying, “the tests pass…so the code must be correct.” Or is it? They don’t tend to put a lot of emphasis on going back and really understand how and why it works, instead relying on the tests as “proof” of correctness.

    This is my major criticism of TDD: I feel that it encourages a ‘micro’ view of the problem as code, putting too much emphasis on emergent design, without enough consideration of the underlying *problem*, its invariants, and so forth. To throw some gasoline on the fire, consider Ron Jeffries attempts at building a sudoku solver versus Peter Norvig’s solution. To me, the salient take-away is that Norvig digs into the problem domain to uncover a solution, while Jeffries attempts to TDD his way to a solution. His code is well-tested, but he ends up bogged down in issues of (very well tested) representation instead of understanding what he was trying to solve.

    Don’t get me wrong: I don’t mean to dismiss TDD as a useful tool and indeed, it is quite useful when applied properly. But I do see a problem when it’s oversold as more than one of many tools in the programmer’s kit. For instance, in this post by Robert C. Martin, in which Martin claims that if you believe that TDD slows down development, you are “living in the stone age”: http://blog.objectmentor.com/articles/2009/10/06/echoes-from-the-stone-age (note my comment towards the end, that links to a Microsoft study that found that, while TDD *did* reduce defect rates, it did so at the expense of slowing down development by 15-35%. See , http://research.microsoft.com/en-us/news/features/nagappan-100609.aspx. Not that Nagappan et al have data; Martin does not).

  848. Thanks, Dan, that is very substantial comment! Here of course is the crux:

    …and so on. Eventually, I’ll converge on some kind of binary search, maybe by first implementing a naive linear search (just to get a test to pass) and then refactoring into the final binary search.

    As we both know perfectly well, the “… and so on” is a massive cheat: the idea that you can add one more special case at a time to your code until a perfect general solution emerges butterfly-like is a complete nonsense — a fairy story. No amount of tests can create understanding: they are two different kinds of thing. It’s like thinking that by taking one step after another “and so on” you can eventually reach the moon.

    Which is why this statement grates with me:

    Personally, I find Bentley’s method more intellectually satisfying than TDD’ing one’s way to an emergent solution.

    The difference isn’t that it’s more satisfying: the difference is, it works.

    No doubt you have heard of “The Feynman algorithm” (actually coined by his colleague Murray Gell-Mann) for solving any problem in three steps?

    1. Write down the problem.
    2. Think very hard.
    3. Write down the solution.

    Although this sometimes thought of a glib advice, it actually maps very well to Bentley’s approach to solving the binary search problem. Step 1, writing down the problem, is how you find the invariant. Step 2, thinking very hard, is constructing the algorithm so that it maintains the invariant and doesn’t fumble the edge-cases. And Step 3, writing down the solution, is incarnating that algorithm in code.

  849. I was going to briefly address a few points. Unfortunately, to paraphrase Blaise Pascal (and others, apparently): I didn’t have time to write a short comment, so I’ve written a long one instead.

    Dan: I completely agree on several points. TDD is a tool, which can be used incorrectly. It does not guarantee correct results. The intent is only to stack to deck in your favor. Please note that the Microsoft study indicates that the defect difference was “60 to 90 percent better” in TDD’d code vs non-TDD’d code, from that link. I’m not sure what ‘quicker’ or ‘slower’ means here, but I’m pretty sure we’re not comparing apples to apples here.

    I’d also like to emphasize Dan’s point about refactoring from a linear search to a binary search. The problem you’re trying to solve with TDD is ‘find the item in this list’. A given solution has a particular performance profile which you can change through various refactoring steps. I will try to put together a blog post on this subject sometime soon, and let Mike decide if he wants to link to it here.

    Mike: one final point about the “and so on” comment. I don’t think anyone is claiming that adding tests will magically create a solution where you could not create one before. Since this is the second time I’ve seen you mention it I’m assuming this is something you’ve seen stated before. My condolences.

    I’d be happy to drop the TDD subject since I’m worried that the ‘T’ and ‘D’ letters are going to fall off my keyboard. If others wish to continue, I am game, but I’m not here to change hearts and minds.

    I fundamentally disagree on one important point, which goes to the heart of something both Dan and Mike wrote. Dan wrote, “thinking about what I was doing a little more rigorously would likely have been sufficient.” This is wrong because it is not actionable. You cannot say “next time I will think harder” and seriously expect to see a different result. The fact is that people make mistakes, and we put lots of processes in place to mitigate that. ‘Thinking better’ is not such a process, it is the result of training and the processes we use.

    There’s a similar problem with the “Feynman Algorithm”. I can only believe this was coined tongue-in-cheek because it contradicts the fact that every other creative act (that I can think of) is an iterative processes. To back me up, here’s to another page by an arbitrary blogger whose credibility exceeds my own by virtue of citing references online. :-) (http://biocurious.com/2008/05/28/the-real-feynman-algorithm) The pertinent quote refers to how Feynman would break apart problems until they were solvable, or until he lost interest. This is not “Thinking very hard.” This is iterations of observing, testing and theorizing. ‘Think very hard’ is not a method or process or algorithm. It’s handwaving, and is completely unhelpful.

    Otherwise, I agree completely. :-)

  850. I’d also like to emphasize Dan’s point about refactoring from a linear search to a binary search. The problem you’re trying to solve with TDD is ‘find the item in this list’. A given solution has a particular performance profile which you can change through various refactoring steps.

    The same is true of D. (That is, whether your Development is Test-Driven or not, you are presumably implementing a routine that has a specific contract, as I discussed in the preconditions and postcondition post.)

    Mike: one final point about the “and so on” comment. I don’t think anyone is claiming that adding tests will magically create a solution where you could not create one before. Since this is the second time I’ve seen you mention it I’m assuming this is something you’ve seen stated before. My condolences.

    Whether or not TDD users actually expect this to happen, its advocates often talk as thought they do. The now well-known case of Ron Jeffries’ Sudoku solver is a fine example. And of course it’s what “test-driven design” means. (I know you’re using TDD to mean test-driven development, which I have much less of an issue with; but I’ve seen the other interpretation, too.)

    I fundamentally disagree on one important point, which goes to the heart of something both Dan and Mike wrote. Dan wrote, “thinking about what I was doing a little more rigorously would likely have been sufficient.” This is wrong because it is not actionable. You cannot say “next time I will think harder” and seriously expect to see a different result.

    No argument here. You need something more specific and actionable than “think harder”. But of course that is exactly what invariants (among other tools give you). And “write more tests” doesn’t, so much.

    There’s a similar problem with the “Feynman Algorithm”. I can only believe this was coined tongue-in-cheek because it contradicts the fact that every other creative act (that I can think of) is an iterative processes.

    Absolutely it was coined tongue-in-cheek. It deliberately romanticises Step 2 for comic effect, making it look like a black box. Or you could think of it as a very high-level function in a program to solve problems, to be refined by top-down programming by implementing the details of the “think very hard” step. So I absolutely agree with the blog-post that you cited. The question is, what techniques can you bring to the sub-problem of thinking very hard? Invariants are one such, and a useful one. TDD is another, and not so useful when dealing with a hard problem (Tests are of course useful in the less demanding role helping us to avoid dumb errors.)

    The pertinent quote refers to how Feynman would break apart problems until they were solvable, or until he lost interest. This is not “Thinking very hard.”

    Sure it is.

    This is iterations of observing, testing and theorizing. ‘Think very hard’ is not a method or process or algorithm. It’s handwaving, and is completely unhelpful.

    Not at all. “Think very hard” is not a recipe, but a warning — a reminder of what is going to be required. It’s the antidote to the seductive fiction that just sitting and churning out enough tests will magically yield a working Sudoku solver.

  851. I think that the answer to the Ron Jeffries solver is that “TDD might not work”, which is perfectly true. I think it’s wrong to generalize to “TDD doesn’t work.”

    Some examples here were created by thinking… and also didn’t work. Does it prove that that thinking doesn’t work? No, only that sometimes it might also not work.

    I use TDD and I like it, but I do it because it has helped me to think more clearly about problems and build solutions stepwise.

    Sometimes the solution is in my head (merely Test-First, not really Test-Driven).

    Sometimes the stepwise specification helps me to see a solution part-way through.

    Sometimes when I have a solution in place with tests, I think I see a better solution. The existing tests can be used to make sure I’m not deluding myself (which I sometimes am).

    TDD is one tool that can help, and it’s useful enough that it shouldn’t be abandoned or maligned because Ron built a poor solver. There are many ways to come up with the wrong answer.

  852. I think that the answer to the Ron Jeffries solver is that “TDD might not work”, which is perfectly true. I think it’s wrong to generalize to “TDD doesn’t work.”

    Excellent summary.

  853. I’ll accept that I should have been a bit more specific in defining what I meant when I said, “applying more rigor to my thought,” but first I will submit that thinking harder is actionable; thought in a field like ours really is action a lot of the time.

    To clarify, I think that if I had been more rigorous, I would have stated the preconditions on the input array. E.g., “we assume the input array is non-NULL and of length zero or more, and that the elements are sorted in ascending order.”

    Many of my comments in this, er, comment thread were in response to a specific comment which stated that my program “makes the case” for TDD. I don’t think that’s true at all; my program makes the case for some kind of rigorous process, and TDD is one of several, but not the only one. As I thought about how I might TDD a solution to the binary search algorithm, it occurred to me that the major value I would get from the TDD approach was, more than anything, a comprehensive set of tests to exercise the code. I do not believe that it would help me in producing the algorithm, really, though it may save me from myself in terms of helping me find edge cases or silly errors (like using unsigned indices when the algorithm is predicated on them being signed…though that itself is a bug).

    Something that kind of nags me about this, however, is the following: many binary search routines fail to account for arithmetic overflow when calculating the index of the midpoint of the array section in question. Well known, and extremely well tested, implementations have suffered from this defect for many years (it wasn’t until memories got large enough that people could, and did, create arrays with enough elements that overflow actually became an issue that these bugs were discover, though they’d been present for many years; in some cases, decades!). In general, do people think that TDD’ing a binary search program would lead to solutions where those bugs were absent? How about in all cases? The average case?

    Personally, I tend to doubt it. I don’t think that the average programmer would take TDD to the extreme necessary to find that class of defect.

    Don’t get me wrong: again, I think that TDD can be useful. But I also think that it is often oversold by its proponents, or treated as a magic bullet. It’s not; it’s just another tool to be used where appropriate, but not the only one.

  854. As I thought about how I might TDD a solution to the binary search algorithm, it occurred to me that the major value I would get from the TDD approach was, more than anything, a comprehensive set of tests to exercise the code.

    But that wouldn’t be test-driven design. It would be test-driven testing. Or, as we used to call it, testing.

    I do not believe that it would help me in producing the algorithm.

    Precisely.

  855. Amee Briola

    Thanks for good info :)

  856. Untested, I commit to this code: https://gist.github.com/2829302
    I think array slices in the language really make this a bit too easy.
    Note that D/Neat slices are start-inclusive, end-exclusive.

  857. I know it’s two years later, but to avoid bias, I must report my program. It seems to be correct. Disclaimer: after running the program, I added “\n”s to the output statements; besides that, I only fixed problem the compiler found.

    module Main where
    
    import Data.Array
    import Text.Printf
    
    binarySearch :: Ord t => Array Integer (t, u) -> t -> Maybe u
    binarySearch a targetKey =
      let searchRange i0 i1 = -- Searches indices i0 <= i < i1
            if i0 >= i1
            then Nothing
            else let mid = div (i0 + i1) 2
                     (key, value) = a ! mid
                 in
                  case compare targetKey key of
                    EQ -> Just value
                    LT -> searchRange i0 mid
                    GT -> searchRange (mid+1) i1
          (aMin, aMax) = bounds a
      in searchRange aMin (aMax + 1)
    
    main :: IO ()
    main =
      let a = listArray (0, 5)
              [ ("alice", 3), ("bob", 8), ("charles", 7), ("darlene", 2)
              , ("elizabeth", 9), ("frederick", 0)
              ]
          result0 = binarySearch a "charles"
          result1 = binarySearch a "daphne"
      in
       do printf "result0 expected: Just 7; actual: %s\n" (show result0)
          printf "result1 expected: Nothing; actual: %s\n" (show result1)
    
  858. I must ALSO report my program! :)


    %% I wondered if I'm in the 10% of devs that can write binary search correctly:
    %% https://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/
    %% The automated test cases at http://www.mac-guyver.com/switham/2010/04/Binary_Search/
    %% (edited for Erlang below) say I am. Well, nice to know :)
    module(binary_search).
    export([binary_search/2]).
    include_lib("eunit/include/eunit.hrl").
    binary_search(List, N) ->
    Length = length(List),
    Middle = (Length + 1) div 2, %% saves us hassle with odd/even indexes
    case Middle of
    0 -> 1; %% empty list -> item not found
    _ ->
    Item = lists:nth(Middle, List),
    case Item of
    N -> Middle; %% yay, found it!
    _ -> case Item > N of
    true -> binary_search(lists:sublist(List, Length Middle), N); %% LT, search on left side
    false -> binary_search(lists:nthtail(Middle, List), N) %% GT, search on right side
    end
    end
    end.


    run:
    erlc binary_search.erl
    erl -noshell -s binary_search test -s init stop
    tests:
    python write_tests.py >binary_search_tests.erl
    erlc binary_search_tests.erl
    all: tests run

    view raw

    Makefile

    hosted with ❤ by GitHub


    $ make all
    python write_tests.py >binary_search_tests.erl
    erlc binary_search_tests.erl
    erlc binary_search.erl
    erl -noshell -s binary_search test -s init stop
    All 4096 tests passed.

    view raw

    sample_run.txt

    hosted with ❤ by GitHub


    #!/usr/bin/env python
    # based on http://www.mac-guyver.com/switham/2010/04/Binary_Search/
    # edited for Erlang :)
    # now essentially prints out a file with all the EUnit tests
    from random import *
    # 0.0 <= random() < 1.0; a <= randrange( a, b ) < b for int(a) < int(b).
    if __name__ == "__main__":
    print "-module(binary_search_tests)."
    print "-include_lib(\"eunit/include/eunit.hrl\")."
    print
    for n in range( 1, 4097 ):
    size = int( 2.0 ** (random() * 8) 1 ) # size can be zero.
    if random() < .5:
    A = range( size ) # every number in range
    else:
    A = [ randrange(size) for i in range(size) ] # random numbers
    A.sort()
    want_there = size > 0 and random() < .5
    if want_there:
    T = A[ randrange(size) ]
    else:
    theres = dict( [ ( x, True ) for x in A ] )
    not_theres = [ x for x in range( 1, size+1 ) if not x in theres ]
    T = not_theres[ randrange( len(not_theres) ) ]
    is_there = T in A
    assert( is_there == want_there )
    print "a_%d_test() ->" % (n)
    print " ?assert%s(binary_search:binary_search(%s, %d) =:= -1)." % (["","Not"][is_there], A, T)
    print

    view raw

    write_tests.py

    hosted with ❤ by GitHub

  859. public static int binarySearch(int[] array, int target) {
    int low = 0;
    int high = array.length – 1;
    int mid;
    while (low <= high) {
    mid = (low + high) / 2;
    if (array[mid] target) {
    high = mid – 1;
    } else {
    return mid;
    }
    }
    return -1;
    }
    }

  860. Pingback: More thoughts… | Scali's OpenBlog™

  861. Followed this here from scali’s blog, done blind and ran across the out of range issue when I tested it, below is the corrected source.

    bool findTInArray( const int T, const int *Arr, const int sizeArr, int &elementOut )
    {
    	int beg=0; 			// 1st index of Arr
    	int end=sizeArr-1;	// Nth index of Arr
    	
    	// we've been passed a zero length array.
    	if( beg==end ) {
    		elementOut = sizeArr;	// return index == to Arr[Nth+1]
    		return false;
    	}
    	
    	// assumption
    	// find only works on arrays sorted with lesser values lower in the array
    	if( Arr[beg] > Arr[end] ) {
    		elementOut = sizeArr;	// return index == to Arr[Nth+1]
    		return false;
    	}
    
    	// invalid value lesser or greater than whole min max values
    	if( T<Arr[beg] || T>Arr[end] ) {
    		elementOut = sizeArr;	// return index == to Arr[Nth+1]
    		return false;
    	}
    	
    	int mid=(end>>1);
    	while( beg!=(end-1) ) {
    		if(Arr[mid]==T) {
    			// yes, found it
    			elementOut = mid;
    			return true;
    		}
    		
    		if(T<Arr[mid]) {
    			// T is less than the mid value
    			end=mid;
    		} else {
    			// T is greater than the mid value
    			beg=mid;
    		}
    		mid=beg+((end-beg)>>1);
    	}
    	
    	return false;
    }
    
  862. On my first attempt, http://pastie.org/5343150, I thought that python slices didn’t copy. Then I found out that they do, so that solution doesn’t meet the log(2)n requirement (though i believe it is algorithmically sound). After realizing that, I wrote http://pastie.org/5343247. I think it’s correct after a bit of testing, but I didn’t have time to be really exhaustive.

  863. Pingback: Binary Search in Scheme | the logic grimoire

  864. long
    bsearch(const std::vector<int>& values, long begin, long end, int key)
    {
        if (begin >= end)
        {
            return -1;
        }
        auto mid = (begin + end) / 2;
        if (key < values[mid])
        {
            return bsearch(values, begin, mid, key);
        }
        else if (key > values[mid])
        {
            return bsearch(values, mid + 1, end, key);
        }
        else
        {
            return mid;
        }
    }
    
  865. Seems to work..?
    Finds all elements in the array, and handles elements outside the array?

    public int Find(int[] arr, int Target) {
    int lower = 0;
    int upper = arr.length;
    if((arr.length == 1) && (arr[0] == Target))
    return 0;
    while(lower arr[mean]) {
    lower = mean+1;
    } else if(Target < arr[mean]) {
    upper = mean;
    } else {
    return mean;
    }
    }
    return -1;
    }

  866. Hope this works:

    function binary_search( T, array ) {
    	var len,
    			len2,
    			mid;
    
    	while ( array.length >= 1 ) {
    		len = array.length,
    		len2 = ~~(len/2)
    
    		mid = array[ len2 ];
    
    		if ( len <= 1 ) {
    			return false
    		}
    
    		if ( T === mid ) {
    			return true
    		}
    		else if ( T < mid ) {
    			array = array.slice(0, len2 );
    		}
    		else if ( T > mid ) {
    			array = array.slice( len2 , len );
    		}
    	}
    
     }
    
  867. Jonathan Bernard

    Several years later, here is my offer. Untested but I have implemented this algorithm in the past. Language is groovy. You can take this and past it in groovysh or save it as a file and source the file from groovysh.

    // Precondition: arr is sorted.
    // This algorithm implements binary search over an integer array, using the
    // natural ordering of integers.
    binSearch  = { int[] arr, int criteria ->
    
      // Search range is defined as b..<e By convention we will consider the index b
      // included in the range and index e excluded from the range.
      int b = 0, e = arr.length
    
      // M will be the midpoint of the range
      int m
    
      // For fun we will track the number of steps it took to find the item. This
      // may also help us spot weird problems (if it takes more steps to find the
      //result than expected).
      int steps = 0
    
      // When b == e we have either found the item or exhausted our search space.
      while (b != e) {
        steps++
    
        // Integer overflow is impossible, since we are doing the addition as longs.
        // No two ints average to a value greater than Integer.MAX_VALUE so we know
        // the value is safe to store in m and use as an array subscript.
        m = ((b as long) + (e as long)) / 2
    
        // Lookup the value so we only index the array once (more for readability
        // than optimization).
        int value = arr[m]
    
        // Midpoint value is greater than the criteria, restrict our search to the
        // left-hand side of the range (b..<m)
        if (criteria < value) e = m 
    
        // Midpoint value is less than the criteria, restrict our search to the
        // right-hand side of the range (m>..e)
        else if (criteria > value) b = m + 1
    
        // If they are equal we have found our item (we can restrict our search
        // space to the one element and stop searching)
        else b = e = m
    
        // Note that every iteration of this loop either concludes with b == e
        // or a strictly smaller range b..<e
      }
    
      // if we found the item, return the index
      if (arr[m] == criteria) return [steps: steps, index: m]
    
      // otherwise the item is not present in this array, return an index of -1
      else return [steps: steps, index: -1]
    }
    
  868. Here’s my submission in Python. I’ve included the testing code too and it passed, so I’m pretty confident it works.

    def binary_search((arr, target)):
      start_index  = 0
      end_index   = len(arr)-1
      while start_index <= end_index:
        current_index = (start_index+end_index)/2
        current_elem = arr[current_index]
        
        if (target == current_elem):
          return current_index
        elif (target > current_elem):
          start_index = current_index + 1
        else:
          end_index = current_index - 1
      
      return -1
    
    #Testing code
    arr0 = []
    arr1 = [1]
    arr2 = [2,4]
    arr3 = [-1,7,15]
    arr4 = [-6,4,9,21]
    
    inputs = [(arr0, 0), (arr1,1), (arr1,2), (arr2,1), (arr2,2), (arr2,3), (arr2,4), (arr2,5), (arr3,-2), (arr3,-1), (arr3,8), (arr3,7), (arr4,-6), (arr4,9), (arr4,21), (arr4,7), (arr4,22)]
    
    exp_outputs = [-1,0,-1,-1,0,-1,1,-1,-1,0,-1,1,0,2,3,-1,-1]
    
    outputs = map(binary_search, inputs)
    
    assert(exp_outputs==outputs)
    
  869. I failed horribly and ended up implementing a “round this number down to the closest number in this set” function, not a binary search function. ^^

    function bsearch(list, value) {
        var index1, index2;
    
        if (list.length === 1) {
            return list[0];
        } else if (list.length === 0) {
            return null;
        }
    
        index1 = 0;
        index2 = Math.floor(list.length / 2);
        if (list[index2] === value) {
            return list[index2]
        } else if (list[index2] < value) {
            return bsearch(list.slice(index2), value);
        } else {
            return bsearch(list.slice(index1, index2), value);
        }
    }
    
  870. My attempt in C#. Written in Visual Studio exactly like reproduced below, then tested. It appears to work fine (tested a few edge cases and then generated 1M random lists, sorted them and threw them at the search, in parallel to Array.FindIndex. No issues found.

            /// <param name="sortedList">
            /// A list sorted in ascending order (monotony not required).
            /// The "sorted" property is not checked, violation may cause a wrong result.
            /// </param>
            /// <param name="element"></param>
            /// <returns>
            /// The index of the first element in sortedList equal to the element given (as determined by the CompareTo method of element),
            /// or -1 if no such element exists.
            /// </returns>
            public static int bsearch<T>(IList<T> sortedList, T element)
                where T : IComparable<T>
            {
                int start = 0;
                int end = sortedList.Count;
    
                while (true)
                {
                    int size = end - start;
                    if (size == 0)
                        return -1;
                    else if (size == 1)
                        return element.CompareTo(sortedList[start]) == 0 ? start : -1;
    
                    int mid = start + (size - 1) / 2;
    
                    if (element.CompareTo(sortedList[mid]) <= 0)
                        end = mid + 1;
                    else
                        start = mid + 1;
                }
            }
    
  871. My attempt, untested at the moment. I will test later and report back in a reply.

    I’m linking to the full commit hash so you know I can’t tamper with it after posting this comment.


    // Given an array of numbers, returns either the index containing that number
    // or `undefined` if the array doesn't contain that number.
    // Assumes that `list` is a non-sparse JavaScript array, that all items in the list are integers,
    // and that the list is sorted in ascending order (obviously)
    // Assumes that `value` is an integer.
    function binarySearch(list, value) {
    // Lower bound = minimum possible index
    var lowerBound = 0;
    // Upper bound = maximum possible index + 1
    var upperBound = list.length;
    // An index in the middle of our range. We will compute this shortly.
    var middleIndex;
    // Loop forever (we will break out of this loop via a return statement)
    while(true) {
    // Is our target range of length 0 (or negative!?)? Then the target value must not be in the list
    if(upperBound lowerBound <= 0) return undefined;
    // Compute the index of the middle of our target range
    // Assumes that upperBound >= lowerBound
    middleIndex = lowerBound + Math.floor((upperBound lowerBound) / 2);
    // Have we found the target value? Great! We're done.
    if(list[middleIndex] === value) return middleIndex;
    if(value < list[middleIndex]) {
    // The middleIndex value is greater than our target value
    // so the target value comes *before* middleIndex
    // upperBound = middleIndex, meaning that our range has everything *up to but not including* middleIndex
    upperBound = middleIndex;
    } else {
    // The middleIndex value is less than our target value
    // so the target value comes *after* middleIndex
    // lowerBound = middleIndex + 1, meaning that our range has everything *after and not including* middleIndex
    lowerBound = middleIndex + 1;
    }
    }
    }

  872. It seems to work. Here’s a link to the Gist with my qUnit tests added. I hope the tests don’t have any bugs…


    // Given an array of numbers, returns either the index containing that number
    // or `undefined` if the array doesn't contain that number.
    // Assumes that `list` is a non-sparse JavaScript array, that all items in the list are integers,
    // and that the list is sorted in ascending order (obviously)
    // Assumes that `value` is an integer.
    function binarySearch(list, value) {
    // Lower bound = minimum possible index
    var lowerBound = 0;
    // Upper bound = maximum possible index + 1
    var upperBound = list.length;
    // An index in the middle of our range. We will compute this shortly.
    var middleIndex;
    // Loop forever (we will break out of this loop via a return statement)
    while(true) {
    // Is our target range of length 0 (or negative!?)? Then the target value must not be in the list
    if(upperBound lowerBound <= 0) return undefined;
    // Compute the index of the middle of our target range
    // Assumes that upperBound >= lowerBound
    middleIndex = lowerBound + Math.floor((upperBound lowerBound) / 2);
    // Have we found the target value? Great! We're done.
    if(list[middleIndex] === value) return middleIndex;
    if(value < list[middleIndex]) {
    // The middleIndex value is greater than our target value
    // so the target value comes *before* middleIndex
    // upperBound = middleIndex, meaning that our range has everything *up to but not including* middleIndex
    upperBound = middleIndex;
    } else {
    // The middleIndex value is less than our target value
    // so the target value comes *after* middleIndex
    // lowerBound = middleIndex + 1, meaning that our range has everything *after and not including* middleIndex
    lowerBound = middleIndex + 1;
    }
    }
    }


    // Test cases
    test("empty list", function() {
    ok(binarySearch([], 4) === undefined);
    });
    test("one-item list", function() {
    ok(binarySearch([3], 3) === 0);
    ok(binarySearch([3], 10) === undefined);
    ok(binarySearch([3], 1) === undefined);
    });
    test("two-item list", function() {
    var list = [5, 10];
    ok(binarySearch(list, 0) === undefined);
    ok(binarySearch(list, 5) === 0);
    ok(binarySearch(list, 7) === undefined);
    ok(binarySearch(list, 10) === 1);
    ok(binarySearch(list, 12) === undefined);
    });
    test("three-item list", function() {
    var list = [5, 10, 15];
    ok(binarySearch(list, 0) === undefined);
    ok(binarySearch(list, 5) === 0);
    ok(binarySearch(list, 7) === undefined);
    ok(binarySearch(list, 10) === 1);
    ok(binarySearch(list, 12) === undefined);
    ok(binarySearch(list, 15) === 2);
    ok(binarySearch(list, 20) === undefined);
    });
    test("four-item list", function() {
    var list = [5, 10, 15, 20];
    ok(binarySearch(list, 0) === undefined);
    ok(binarySearch(list, 5) === 0);
    ok(binarySearch(list, 7) === undefined);
    ok(binarySearch(list, 10) === 1);
    ok(binarySearch(list, 12) === undefined);
    ok(binarySearch(list, 15) === 2);
    ok(binarySearch(list, 17) === undefined);
    ok(binarySearch(list, 20) === 3);
    ok(binarySearch(list, 40) === undefined);
    });
    var totalIterations = 500000;
    var chunkSize = 1000;
    for(var i = 0; i < totalIterations / chunkSize; i++) {
    test("many random lists of random length with random contents, searching for random numbers", function() {
    var list, length, j, value, listContainsValue, result;
    for(var i = 0; i < chunkSize; i++) {
    list = [];
    length = (Math.random() * 300)|0;
    for(j = 0; j < length; j++) {
    list.push((Math.random() * 200)|0);
    }
    list.sort(function(a, b) {return a < b ? 1 : a > b ? 1 : 0});
    value = (Math.random() * 200)|0;
    listContainsValue = false;
    for(j = 0; j < length; j++) {
    if(list[j] === value) {
    listContainsValue = true;
    break;
    }
    }
    result = binarySearch(list, value);
    ok(result !== undefined || listContainsValue === false, '' + value + ' in ' + JSON.stringify(list));
    ok(result === undefined || list[result] === value);
    }
    });
    }

    view raw

    tests.js

    hosted with ❤ by GitHub

  873. package Binary_Search is
       
       type Int_Array is array (Natural range <>) of Integer;
       Value_Not_Found : exception;
       
       function Binary_Search (A : in Int_Array; X : Integer) return Natural 
         with Post => (A (Binary_Search'Result) = X);
       
    end Binary_Search;
    package body Binary_Search is
       function Binary_Search (A : in Int_Array; X : Integer) return Natural 
       is
          First : Natural := A'First;
          Last : Natural := A'Last;
          Middle : Natural;
       begin
          pragma Overflow_Mode (Eliminated);
          if A'Length = 0 then
             -- Null array
             raise Value_Not_Found;
          end if;
          loop
             Middle := (First + Last) / 2;
             -- Loop invariant: First <= Middle <= Last
             if A(Middle) = X then 
                return Middle;
             elsif A(Middle) > X then
                -- Last can't equal Middle, since / rounds down or towards zero
                Last := Middle;
             elsif A(Middle) < X then
                -- If Last - First > 1, then Middle >= First + 1.
                -- If Last = First or Last = First + 1 then Middle := First
                -- which will only happen if it's the first time through the
                -- loop and then will be caught below.
                First := Middle;
             end if;
             if First = Last then
                raise Value_Not_Found;
             end if;
             if First + 1 = Last then
                if A (First) = X then
                   return First;
                elsif A (Last) = X then
                   return Last;
                else
                   raise Value_Not_Found;
                end if;
             end if;
          end loop;
       end Binary_Search;    
    end Binary_Search;
    

    Ada2012 with a GNAT specific pragma (to do the index math in infinite precision math; it’d be quicker to do it in 64-bit math, but more architecture specific.) It worked first time, and made me wonder if I should have worked on the test drivers the same way, since they went through a serious bug/test/bug cycle.

    Interesting performance issue. Finding an item in the array is O(log N); not finding an item in the array is practically constant time. The search in a million element array averaged 0.6 µs; the exception throw and catch ran 14 µs.

  874. Javascript implementation, seems to work fine. Of course I followed the rules (i.e. once I started testing I didn’t change anything):

    function bsearch(array, value) {
    	if (array.length == 0) return -1;
    	
    	var min = 0, max = array.length-1;
    	
    	while (max-min >= 2) {
    		var mid = (max+min)>>>1;
    		if (array[mid] == value) {
    			return mid;
    		} else if (value < array[mid]) {
    			max = mid-1;
    		} else {
    			min = mid+1;
    		}
    	}
    	if (array[min] == value) return min;
    	if (array[max] == value) return max;
    	if (value < array[min]) {
    		return ~min;
    	} else if (value < array[max]) {
    		return ~max;
    	} else {
    		return ~max-1;
    	}
    }
  875. Tim Holland

    Posted before testing:
    def binary_search(l, elem):
    if len(l) == 0:
    return None
    x = len(l) / 2
    if elem == l[x]:
    return x
    elif elem < l[x]:
    return binary_search(l[:x], elem)
    else:
    return binary_search(l[x+1:], elem)

  876. @Tim Holland: Python 2, I presume? (It fails under Python 3 because len(l) / 2 is a float, not an int.) The use of list slicing causes it to run slower (20%, on my tests) then the obvious linear algorithm:

    def linear_search(l, elem):
        for x in range (len(l)):
            if l[x] == elem:
                return x;
        return None
    
  877. Pingback: Binary Search: Random Windowing Over Large Sets | The Intellectual Wilderness

  878. Below my solution in python. Written blindly but tested before submission. Was missing the “break” statement before submission so it was outputting the correct values but would run into an infinite loop of outputting correct values for most cases. Guess I am among the 90% then.

    find=955
    input=[1,2,3,5,6,12,168,938,939,943,955]
    right=len(input)-1
    left=0
    while(right-left != 0):
        middle=(right+left)/2
        if (input[middle]>find):
            if (middle!=right):
                right=middle
            else:
                right=middle-1       
        elif (input[middle]<find):
            if (middle!=left):
                left=middle
            else:
                left=middle+1
        else:
            print "%d found at index %d, verification %d" % (find,middle,input[middle])
            break
    if (left==right):
        if input[left]==find:
            print "nice try, found at %d" % (left)
        else:
            print "not found"
    
  879. Also I notice that I do not check for empty list input. One-element lists should work though.

  880. Failed on the first try:
    http://pastebin.com/sEiPCBBY

    $ ./bsearch 5
    array[4] == 4 (argument was: 5)

    Foiled by a fencepost error it seems.

  881. Pingback: Kompetisi Master Chef | Catatan Kuliah

  882. int search(int array[], int size, int val, int &n) {
      int min = 0;
      int max = size - 1;
      int cell;
    
      cout << sizeof(array) << " " << sizeof(*array) << endl;
    
      while (1) {
        cout << min << " " << max << endl;
        cell = (min + max) / 2;
        if (array[cell] > val) {
          max = cell - 1;
        }
        else if (array[cell] < val) {
          min = cell + 1;
        }
        else if (array[cell] == val) {
          n = cell;
          return 0;
        }
        else {
          return 1;
        }
      }
    
      return 0;
    }
    

    I guess I’m in the 90%! Works if the number is found, fails otherwise, oops!

  883. Tillmann Rendel

    Here’s a version in Haskell that seems to work fine:

    find :: Ord a => a -> (Int -> a) -> Int -> Int -> Maybe Int
    find value array index 0
      = Nothing
    find value array index 1
      = if array index == value
        then Just index
        else Nothing
    find value array index length
      = if value < array (index + half)
        then find value array index half
        else find value array (index + half) (length - half)
      where half = length `div` 2
    

    The parameters of find are: The value to search, the function that accesses an array element by index, the first index in the part of the array where the value must be, and the length fo the part of the array where the value must be. The result is either Just the index of the value if the value has been found, or Nothing if the value has not been found.

  884. Here’s a pretty ugly c# version that seems to work first try.

    {source}
    public static int Search(int val, int[] a)
    {
    if (a == null || a.Length == 0)
    return -1;

    int beg = 0;
    int end = a.Length – 1;
    int mid = end / 2;

    while (true)
    {
    if (beg == end)
    {
    if (a[beg] != val)
    return -1;
    else return beg;
    }
    else if (beg == end – 1)
    {
    if (a[beg] == val)
    return beg;
    if (a[end] == val)
    return end;
    else return -1;
    }

    if (a[mid] == val)
    return mid;
    else if (a[mid] < val)
    beg = mid;
    else end = mid;

    mid = ((end – beg) / 2) + beg;
    }
    {/source}

  885. I’m a couple of years late, but I’m proud to report that the code I came up with in about five minutes is incorrect. Here’s what I wrote:

    def binary_search(array, item):
        lower = 0
        upper = len(array) - 1
        while upper > lower:
                middle = (lower + upper) // 2
                if array[middle] == item:
                        return middle
                elif array[middle] < item:
                        lower = middle + 1
                elif array[middle] > item:
                        upper = middle - 1
        return None
    

    The test in the while loop should be “upper >= lower”. As is, if the range gets down to just one element, then the loop terminates and we return None, even if that element is the element searched for. Changing the comparison seems to make everything work.

  886. Well, congratulations on both getting so close to a correct routine first time out, and for being honest about the one-character error!

  887. This is my C# solution. It’s certainly not perfect; however, I felt confident in it.

    private static bool BinarySearch(int[] sortedHaystack, int needle)
    {
    int leftBoundary = 0;
    int rightBoundary = sortedHaystack.Length – 1;

    while (leftBoundary <= rightBoundary)
    {
    int pivotIndex = (int)(((long)leftBoundary + rightBoundary) / 2);
    int pivotValue = sortedHaystack[pivotIndex];

    if (pivotValue == needle)
    return true;

    if (leftBoundary == rightBoundary)
    break;

    if (needle < pivotValue)
    rightBoundary = pivotIndex;
    else
    leftBoundary = pivotIndex + 1;
    }

    return false;
    }

  888. C++ solution:

    int bsearch(int *array, unsigned size, int value)
    {
       unsigned low = 0;      // inclusive
       unsigned high = size;  // exclusive
       while(low<high)
       { 
          // integer division rounds down, so mid >= low and mid < high
          unsigned mid = (low+high)/2;
          // if high-low is odd, mid will be the exact middle item
          // if high-low is even, mid will be the first item of the upper half
          if(array[mid]<value)
             low = mid+1;  // mid is known to be too low, so add one
          else if(array[mid]>value)
             high = mid;
          else  // not < or >, so must be ==
             return mid;
       } 
    
       // possible range became empty, return a "not found" indicator
       return -1; 
    }
    

    I wrote the code in about 5 minutes and spent another 10 minutes verifying its correctness. Since no official test cases were given, I wrote my own. I almost thought I had failed when I tested it, but it turned out my testing code had a bug in it.

  889. It’s amazing how often “my testing code had a bug in it” comes up! On the subject of “official” test cases, you might be interested in the follow-up post What does it take to test a sorting routine?; and on my scepticism about testing as a universal panacea, Testing is not a substitute for thinking (binary search part 3).

    ENjoy!

  890. Addendum:

    Come to think of it, my solution is also perfectly valid C code, even though I used C++ in the testing part.

    The rules didn’t state [i]how[/i] to deal with repeated elements, so my solution returns the index of one of them, but it’s unspecified which one.

    To clarify things, I consider my solution to be correct based on the limited testing I put it through.

  891. Posting this before testing whether it actually works. I’m quite confident that it will work because I executed a few test cases in my head and fixed a number of bugs I found.

    # Returns index of value in sorted array or None if not found
    # range_min: inclusive
    # range_max: exclusive
    def bsearch(value, array, range_min=0, range_max=None):
        if range_max == range_min:
            return None
        if range_max is None:
            range_max = len(array)
        mid_index = range_min + (range_max - range_min) // 2
        mid_elem = array[mid_index]
        if mid_elem < value:
            return bsearch(value, array, mid_index+1, range_max)
        if mid_elem > value:
            return bserach(value, array, range_min, mid_index)
        return mid_index
    
  892. Lol. Apparently I missed a typo in the function name in the second recursive function call. Just s/bserach/bsearch/ and the whole thing seems to be fully working!

  893. And no! It’s borked. It fails the case of an empty input array because the two initial conditionals with their contents are in the wrong order. That taken care of, it works for everything I have so far thrown at it.

  894. So far seems like success :) http://pastebin.com/qQmad9QR

  895. Recursive Java solution:

    http://pastebin.com/kAeYyr1j

    According to my testing here it all seems to work OK. Although I did have to fix a typo to make it run properly, it wasn’t part of the algorithm itself (I was calling the wrong version of search recursively), so I’m not sure whether it counts as success or not…?

  896. Congratulations, Jules, I call that success!

  897. I think that the test condisions are unrealistic. You test your code before you claim that you are done. If 90% fail at your conditions, it does not mean they cannot write a binary search.

    According to your conditions, I have to report failure.
    Time to write binary search in the first place: 5 minutes.
    Time to write test: 20 minutes (had to lookup how from C++11 works, we have our own random generator from pre-C++11-times at work)
    Time to bugfix the bsearch routine: another 5 minutes.

    Code that I claim to work

    template<typename Iterator>
    bool bsearch (Iterator begin, Iterator end, int el)
    {
        if (end <= begin)
            return false;
        else if (end == begin+1)
            return *begin == el;
        Iterator middle = begin + (end - begin) / 2;
        if (*middle>el)
            return bsearch (begin, middle, el);
        else if (*middle<el)
            return bsearch (middle, end, el);
        else
            return true;
    }
    

    Note that end is one after the last element.

    In the first place, I tought about ranges of size 1, but incorrectly assumed that they require no special treatment.

  898. Pingback: Binary Search : can you write it correctly? | huangdah

  899. binary = (arr, T, left = 0, right = arr.length - 1) ->
      if right - left <= 1
        return -1 if arr.length is 0
        return left if arr[left] is T
        return right if arr[right] is T
        return -1
      middle = parseInt((left + right) / 2, 10)
      return middle if arr[middle] is T
      if T < arr[middle]
        right = middle
      else
        left = middle
      return binary(arr, T, left, right)
    
    arr = ["a", "b", "c", "d"]
    T = "c"
    binary(arr, T)
    
    # (0, 3) -> 1
    # (1, 3) -> 2 !
    
    arr = ["a", "b", "c", "d", "e"]
    T = "a"
    binary(arr, T)
    
    # (0, 4) -> 2
    # (0, 2) -> 1
    # (0, 1) -> 0 !
    
    arr = ["a", "b", "c", "d", "e"]
    T = "e"
    binary(arr, T)
    
    # (0, 4) -> 2
    # (2, 4) -> 3
    # (3, 4) -> 4 !
    
    arr = ["a", "b", "c", "d", "e"]
    T = "z"
    binary(arr, T)
    
    # (0, 4) -> 2
    # (2, 4) -> 3
    # (3, 4) -> -1 !
    
    arr = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
    T = "f"
    binary(arr, T)
    
    # (0, 8) -> 4
    # (4, 8) -> 6
    # (4, 6) -> 5 !
    
    arr = ["a", "b", "y", "z"]
    T = "k"
    binary(arr, T)
    
    # (0, 3) -> 1
    # (1, 3) -> 2
    # (1, 2) -> -1 !
    
    # possible optimization: right = middle - 1; left = middle + 1, because we know that middle isn't the index we're looking for
    
  900. i succeded BUTTTTT i failed because of a accidentaly typing a wrong name, but i fixed it later
    aka i typed a – b instead of b – a, does that count as success?

    the code + a unit test
    {source}

    import math

    def binarysearch(pk, findMe):

    ret = -1
    minBound = 0
    maxBound = len(pk)
    while True:
    middle = math.floor( minBound + maxBound / 2 )
    if pk[middle] > findMe:
    maxBound = middle -1
    if pk[middle] < findMe:
    minBound = middle + 1
    if pk[middle] == findMe:
    ret = middle
    break
    if (maxBound – minBound) <= 0: #if we have a both min and max the same, but middle happens to be a match, this code is never reached ;)
    break

    # if ret == -1 then didnt find

    return ret

    def unitTest(arr , find):
    loc = binarysearch(arr , find)
    if arr[loc] == find:
    return True
    else:
    return False

    arr = [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10]
    find = 3
    print( unitTest(arr, find) )
    print( binarysearch(arr , find) )

    {/source}

    python 3 btw/ ftw

  901. Well, I’m afraid by the rules of the competition, that can’t be counted a success. But you can consider yourself a moral victor.

  902. dougcosine

    Five years later and you’re still getting replies! I thought this page would probably be dead, but it isn’t so I’ll give it a shot later.

    However, I think that the best lesson to take from this statistic is that testing is an integral part of coding. A better measure of someone’s skill as a coder, in my opinion, is their ability to develop rigorous tests for their code and their ability to use the results of those tests to fix their code.

  903. Yes, Doug, it’s amazing that this page keeps on slowly but surely racking up the comments! I wonder if it will eventually reach 1000? Some kind of celebration would be in order.

    I don’t really agree, though on the lesson you take from this exercise. My own conclusion is in a follow-up piece, Testing is not a substitute for thinking (binary search part 3), with some specifics following in Writing correct code, part 1: invariants (binary search part 4a). I hope you find them helpful, or at least interesting.

  904. Thanks for the post! Here’s my attempt after 20mins in notepad. The code hasn’t been tested yet (apart from manually following through a few cases I could think of).

    def search(ary, val, top = nil, bottom = nil)
    	raise 'invalid array' unless ary.present?
    	top = ary.last unless top.present?
    	bottom = ary.first unless bottom.present?
    	
    	mid = bottom + ((top - bottom) / 2)
    	if val < mid && val >= bottom
    		top = mid
    		return search(ary, val, top, bottom)
    	elsif val > mid && val <= top
    		bottom = mid
    		return search(ary, val, top, bottom)
    	elsif mid == val
    		return mid
    	end
    	raise 'not found'
    end
    
  905. It’s been a few years; time to try this again. This is in Go (no unsigned issues here; array indices are defined to fit into the ‘int’ type):

    : spitfire; date;gofmt >bsearch.go;go run bsearch.go;date
    Tue Jun 16 18:05:22 EDT 2015
    package main
    
    import "fmt"
    
    func Bsearch(a []int, v int) (bool, int) {
    	l := 0
    	r := len(a)-1
    	for l <= r {
    		m := l + (r-l)/2
    		if a[m] == v {
    			return true, m
    		} else if a[m] < v {
    			l = m + 1
    		} else {
    			r = m - 1
    		}
    	}
    	return false, -1
    }
    
    func Trial(a []int, v int) {
    	has, index := Bsearch(a, v)
    	fmt.Printf("Bsearch(a, %d) = %v", v, has)
    	if has {
    		fmt.Printf(", %d", index)
    	}
    	fmt.Printf("\n")
    }
    
    func main() {
    	a := []int{0, 1, 2, 4, 10}
    	for k := -1; k <= 11; k++ {
    		Trial(a, k)
    	}
    }
    Bsearch(a, -1) = false
    Bsearch(a, 0) = true, 0
    Bsearch(a, 1) = true, 1
    Bsearch(a, 2) = true, 2
    Bsearch(a, 3) = false
    Bsearch(a, 4) = true, 3
    Bsearch(a, 5) = false
    Bsearch(a, 6) = false
    Bsearch(a, 7) = false
    Bsearch(a, 8) = false
    Bsearch(a, 9) = false
    Bsearch(a, 10) = true, 4
    Bsearch(a, 11) = false
    Tue Jun 16 18:07:58 EDT 2015
    : spitfire;
    
  906. This is the code I had before testing https://gist.github.com/mlehmk/46fe50a618e59ed77792

    then testing it with random arrays and random values comparing it with the binary search implementation in .NET Framework. I found out that the results of both implementations differ, yet I re-read the specs and it is still within. So I’d call it a success.

  907. I concur — congratulations!

  908. So…the other 90% are smart enough to use a library that does this for them? Programming != Computer Science! Most programmers do not need to be computer scientists. It’s like comparing engineers with physicists. We need computer scientists to figure this stuff out and write amazing libraries for programmers to use. Programmers are the workhorses. I’m never going to need to write a “binary search” in my professional life, but I’m undoubtedly relying on software written by others to do so. It’s not that I’m trying to put down programmers–most computer scientists are so in their head about things they couldn’t write a decent practical application if their life depended on it. I just hate it when people try to pretend that these two divisions do not exist.

  909. meh, I discuss these issues in some detail in the subsequent posts.

    • Common bugs and why exercises matter (binary search part 2)
    • Testing is not a substitute for thinking (binary search part 3)
    • Writing correct code, part 1: invariants (binary search part 4a)
    • Writing correct code, part 2: bound functions (binary search part 4b)
    • Writing correct code, part 3: preconditions and postconditions (binary search part 4c)
  910. bool binsearch(int key, int array[], int min, int max, int end)
    {
    int mid1 = 0;
    int mid = 0;
    if(max < min)
    {
    end = 0;
    return false;
    }
    else
    {
    mid1 = max / 2;
    mid = round(mid1);
    if(array[mid] < key)
    {
    return binsearch(key, array, mid + 1, max, end);
    }
    else if(array[mid] < key)
    {
    return binsearch(key, array, min, mid – 1, end);
    }
    else
    {
    end = end + 1;
    return true;
    }
    }
    }

  911. David Starner

    mmoore, “end” is only an in value, not an in out value in your code. Did you mean to have a & there, or did the blog delete it? Your comparisons both point the same direction, but even if that’s fixed, end returns its start value plus one.

  912. I think I got it right on the first try. Admittedly, I had to fix an error when I compiled it (I forgot to pass `list` and `elem` to the recursive `binary_search_range` call, thus it wouldn’t compile), but the algorithm itself looks solid to me.

    /*
    Recursive binary search. Find element `elem` in list in list index range [lower, upper).
    */
    bool binary_search_range(const std::vector &list, const int &elem, unsigned int lower, unsigned int upper) {
    unsigned int range_size = upper – lower;
    if(range_size == 0)
    return false;

    // if `range_size == 1`, then `medium == lower`
    unsigned int medium = lower + (range_size / 2);

    if(elem == list) {
    return true;
    } else if(elem < list) {
    return binary_search_range(list, elem, lower, medium);
    } else {
    return binary_search_range(list, elem, medium + 1, upper);
    }
    }

    bool binary_search(const std::vector &list, const int &elem) {
    return binary_search_range(list, elem, 0, list.size());
    }

  913. def BinaryChop2(key, sortedArray):
    startIdx = 0
    endIdx = len(sortedArray) – 1
    while endIdx >= startIdx:
    midIdx = (startIdx+endIdx)/2
    midValue = sortedArray[midIdx]
    if midValue == key:
    return midIdx
    elif midValue > key:
    endIdx = midIdx – 1
    continue
    else:
    startIdx = midIdx + 1
    return -1

    (Python code)

  914. Pingback: Recursive PowerShell Binary Search Algorithm : MIKE MORAWSKI | Programming & Tech Blog

  915. Brandon Rivera-Melo

    I thought this was pretty simple. I think it works pretty much the same as what everyone here has written. At first I thought not “testing” it was kind of a weird challenge. We always test our code when we write it- that’s just part of the workflow. I also thought not looking at how other people have done this is a weird thing to include in a challenge, because that’s promotes a scenario of self-dependence which is both bad practice and unrealistic.

    That being said, I still thought this would be a good challenge to test my “Head Simulator” – how well I’m able to imagine alternate scenarios and keep them all in line together. So even though we’re all still testing with our heads (and I think computers are objectively better testers for this than any human… obviously) I wanted to see how good my head was- not just see if I could write this code. And I hope I don’t sounds conceited or self-aggrandizing when I say this, but getting this code to work is easy. It’s not hard to properly conceptualize and verify. Anyway, here’s mine, in C#

    Inputs: input a range and a number for the routine to guess.
    Goal: return the number of guesses

    int GuessTheNumber(int[] range, int numberToGuess){

    if (numberToGuessrange[1]){
    throw new SystemException(“The numberToGuess value is out
    of range. Try a number w/i the range you’ve entered or try
    expanding your range to include the number you’d like to
    guess.”);
    }

    int midpoint = -999999999;
    int numberOfGuesses = 0;

    while (numberToGuess != midpoint){
    midpoint = (range[1]+range[0])/2;
    numberOfGuesses++;
    range = numberToGuess > midpoint ? new int[]{midpoint
    ,range[1]} : new int[] {range[0],midpoint};
    }

    return numberOfGuesses;
    }

    After writing this, I realized there’s still 3 ways to break this code.
    1. Input a range in reverse order ( range = new int[]{10,0})
    2. Input range = new int{ -999999999 , -999999999}
    3. Input range with null values at range [0] and range [1]

    here’s my fixed version then. As far as I’m aware, it’s bug-proof:

    int GuessTheNumber(int min, int max, int numberToGuess){

    if (min > max){
    int holder = min;
    min = max;
    max = holder;
    }
    if (numberToGuessmax){
    throw new SystemException(“Put the numberToGuess value within your range.”);
    }
    else if (min == max){
    throw new SystemException(“Broaden your horizons”);
    }

    int midpoint = -999999999;
    int numberOfGuesses = 0;

    while (numberToGuess != midpoint){
    midpoint = (min + max)/2;
    numberOfGuesses++;
    min = numberToGuess midpoint ? max : midpoint;
    }

    return numberOfGuesses;
    }

    Guess I should have taken a break from the screen for a bit before committing prematurely to it. I got cocky and reward-hungry. But I still think my first pass is pretty effective / simple code for being self taught! Just gotta work on attitudinal things and properly defining my end product (one that cannot be accidentally mis-used) now :D

    Always open to feedback too :D

  916. Well, Brandon, it seems your code has become corrupted in posting — not an unusual problem, unfortunately. Please carefully re-read the instructions at the end of the post on how to format your comment, and re-post the code.

  917. Brandon Rivera-Melo

    alright, found an online formatter (http://codeformatter.blogspot.com/) .
    Let’s see how this works:

    1:  int GuessTheNumber(int min, int max, int numberToGuess){  
    2:       if (min > max){  
    3:            int holder = min;  
    4:            min = max;  
    5:            max = holder;  
    6:       }  
    7:       if (numberToGuessmax){  
    8:            throw new SystemException(“Put the numberToGuess value within your range.”);  
    9:       }  
    10:       else if (min == max){  
    11:            throw new SystemException(“Broaden your horizons”);  
    12:       }  
    13:       int midpoint = -999999999;  
    14:       int numberOfGuesses = 0;  
    15:       while (numberToGuess != midpoint){  
    16:            midpoint = (min + max)/2;  
    17:            numberOfGuesses++;  
    18:            min = numberToGuess midpoint ? max : midpoint;  
    19:       }  
    20:       return numberOfGuesses;  
    21:  }  
    
  918. Brandon Rivera-Melo

    And thank you for the suggestion, Mike! Much appreciated.

  919. Carl Richard Dumdum

    hahaha.. a recursive solution came up to my mind :D

    int bsearch(int *arr, int last_index, int item, int first_index = 0){
    if(first_index > last_index){return -1;}
    int mid_index = first_index + (last_index – first_index)/2;
    if(item == arr[mid_index]){return mid_index;}
    else{
    if(item < arr[mid_index]){
    return bsearch(arr, mid_index-1, item, first_index);
    }
    else{
    return bsearch(arr, last_index, item, mid_index+1);
    }
    }

    }

  920. {source}#!/usr/bin/env python

    ”’Binary Search Implementation in Python.

    This is an implementation of binary search in python.

    Author: Bryan Apperson
    Date: 11/07/2015
    ”’

    def binarysearch(target, data):
    ”’This function performs a binary search on the input set

    The input must be an ordered list (array) and the function will find
    input in using binary search.
    ”’
    binrange = (0, (len(data) – 1))
    found = False
    numsteps = 0
    start, end = binrange
    if target > data[end] or target < data[start]:
    return None, None, None
    while start <= end and not found:
    numsteps = numsteps + 1
    indexlocation = (start + end) // 2
    if data[indexlocation] == target:
    found = True
    else:
    if target < data[indexlocation]:
    end = indexlocation – 1
    else:
    start = indexlocation + 1
    if target != data[indexlocation]:
    return None, None, None
    return target, indexlocation, numsteps

    data = [1, 2, 3, 4, 6, 7, 8]
    targetlist = [1, 2, 3, 4, 5, 6, 7, 8, 12]
    for elem in targetlist:
    target = elem
    result = (binarysearch(target, data))
    targetelem, indexlocation, numsteps = result
    printout = ('Target ' + str(targetelem) + ' is at index ' +
    str(indexlocation) + ' of list ' + str(data) + '.' +
    ' Found in ' + str(numsteps) + ' steps.')
    print (printout){/source}

  921. Here is my C++11 tail recursive solution.

    I have to admint I had it WRONG initally (used m++ instead ++m).
    :(

    bool bin_search(vector::iterator l, vector::iterator r, int val)
    {
    if(l==r) return false;
    auto m=l+distance(l,r)/2;

    if(*m==val)return true;
    if(*mval)r=m;

    return bin_search(l,r,val);
    }

  922. ah I forgot the {source} wrapping: here once again:
    {source}
    bool bin_search(vector::iterator l, vector::iterator r, int val)
    {
    if(l==r) return false;

    auto m=l+distance(l,r)/2;

    if(*m==val)return true;
    if(*mval)r=m;

    return bin_search(l,r,val);
    }
    {/source}

  923. aah, last try:

    My C++11 tail recursive solution:

    bool bin_search(vector::iterator l, vector::iterator r, int val)
    {
    if(l==r) return false;

    auto m=l+distance(l,r)/2;

    if(*m==val)return true;
    if(*mval)r=m;

    return bin_search(l,r,val);
    }

  924. aargh! wordpress still eats parts of my code, forget it..

  925.  bool bin_search(vector<int>::iterator l, vector<int>::iterator r, int val)  
     {  
       if(l==r) return false;  
       auto m=l+distance(l,r)/2;   
       if(*m==val)return true;  
       if(*m<val)l=++m;  
       if(*m>val)r=m;  
       return bin_search(l,r,val);  
     }  
    
  926. Pingback: Can you write a binary search?二分 | Malash's Blog

  927. Never tested.
    Specially made for a date calendar in less than 10 minutes.
    It searches for the year a day is in(e.g. lowest number higher than)
    public static int YearOf (int dayID)
    {
    int a = 0;
    int b = yearOffsets.Length – 2;
    int mid;
    while (a > b) {
    mid = (a + b) / 2;
    int year = yearOffsets [mid];
    int nextYear = yearOffsets [mid + 1];
    if (dayID = nextYear) {
    a = mid;
    continue;
    }
    return mid;
    }
    return -1;
    }

  928. wordpress ate most of my code….

  929. It will do that: check carefully the instructions on posting code at the end of the article.

  930. Hovhannes Movsisyan

    int binary_search(const int a[], int n, int x)
    {
    int lo, hi, mid;
    for(lo = 0, hi = n-1; a[mid = (hi+lo)/2]!=x && lo x ? lo = mid+1 : hi = mid-1)
    ;
    if(lo<=hi)
    return mid;
    return -1;
    }

  931. Jeffrey Waldron
    private static int Search()
    {
    	if (numbers.Count == 0)
    		return NOTFOUND;
    
    	return RecursiveSearch(0, numbers.Count - 1, numbers.Count / 2);
    }
    
    private static int RecursiveSearch(int start, int end, int index)
    {
    	if (numbers[index] == value)
    		return index;
    	else if (index < start)
    		return NOTFOUND;
    	else if (index > end)
    		return NOTFOUND;
    	else if (value < numbers[index])
    		return RecursiveSearch(start, index - 1, start + (index - start) / 2);
    	else
    		return RecursiveSearch(index + 1, end, index + (end + 1 - index) / 2);
    }
    
  932. The Thing From Another Dimension
    def bsearch(elts, key):
        start = 0
        end = len(elts) - 1
    
        while end > start:
            if elts[start] == key:
                return start
            if elts[end] == key:
                return end
    
            mid = (end + start) / 2
            test = elts[mid]
            if key == test:
                return mid
            elif key > test:
                start = mid + 1
            else:
                end = mid - 1
    
        return None
    

    About to test!

  933. The Thing From Another Dimension

    Tested and works (by induction :P)!

    print bsearch([], 0)
    print bsearch([0, 1], -1)
    print bsearch([0, 1], 0)
    print bsearch([0, 1], 1)
    print bsearch([0, 1], 2)
    print bsearch([0, 1, 2], -1)
    print bsearch([0, 1, 2], 0)
    print bsearch([0, 1, 2], 1)
    print bsearch([0, 1, 2], 2)
    print bsearch([0, 1, 2], 3)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], -1)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 7)
    print bsearch([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)

    None
    None
    0
    1
    None
    None
    0
    1
    2
    None
    None
    2
    3
    4
    7
    None

  934. Python user here. Pretty sure this is a success on 1st attempt:
    {source}
    def bf(elem, lst):
    list_copy = list(lst)
    list_copy.sort()

    return bin_find(elem, list_copy)

    def bin_find(elem, lst, begin=None,end=None):
    if begin == None:
    begin = 0
    if end == None:
    end = len(lst) – 1

    if end – begin < 0:
    return False

    mdl = (begin + end) / 2

    if lst[mdl] == elem:
    return True
    elif lst[mdl] elem:
    return bin_find(elem, lst, begin,mdl-1)
    {/source}

    Note: bf is just a handy helper function I wrote, so that I could run a few tests with arbitrary (not ordered) arrays having finished my code.

  935. Did it ! (2 attempts …:( )
    1rst attempt : works if element is in array
    2nd attempt : works all the time
    Python + recursion :
    {source}
    #binary search
    def bsearch(sortedArray, T):
    index = -1
    if len(sortedArray) != 0:
    if len(sortedArray) == 1 :
    if sortedArray[0] == T:
    index = 0
    else:
    middleElemIdx = len(sortedArray) / 2

    if T == sortedArray[middleElemIdx]:
    index = middleElemIdx
    elif T > sortedArray[middleElemIdx]:
    f = bsearch(sortedArray[middleElemIdx+1:], T)
    if f != -1 :
    index = 1 + middleElemIdx + f
    else:
    index = -1
    else:
    f = bsearch(sortedArray[:middleElemIdx], T)
    if f != -1 :
    index = f
    else:
    index = -1
    return index

    ##### TEST CODE ####
    array = [0, 1, 2, 3, 4, 5 ,6, 7, 8, 9, 10]
    for i in array:
    print ‘search ‘ + str(i) + ‘ : ‘ + str(bsearch(array, i))
    print str(bsearch(array, -1))
    print str(bsearch(array, 11))

    array = [0, 8, 9, 12, 75, 115, 225, 1235, 44568]
    for i in array:
    print ‘search ‘ + str(i) + ‘ : ‘ + str(bsearch(array, i))
    print ‘search ‘ + str(-1) + ‘ : ‘ + str(bsearch(array, -1))
    print ‘search ‘ + str(50000) + ‘ : ‘ + str(bsearch(array, 50000))
    print ‘search ‘ + str(226) + ‘ : ‘ + str(bsearch(array, 226))
    {/source}

  936. Success on first try. Reentrant-safe, no unnecessary data on stack, no ineffective doublechecks on values because of wrong sublists.

    public class LogSearch
    {
    public static int FindIndex(IEnumerable list, T item) where T : IComparable
    {
    var logSearch = new LogSearch();
    logSearch.item = item;
    logSearch.FindIndex(list, 0);
    return logSearch.index;
    }

    protected int index = -1;
    protected IComparable item;
    protected void FindIndex(IEnumerable list, int sumIndex) where T : IComparable
    {
    if (!list.Any())
    return;
    var midIndex = list.Count() / 2;
    var midItem = list.ElementAt(midIndex);
    if (midItem.CompareTo(item) 0)
    FindIndex(list.Take(midIndex), sumIndex);
    else
    index = sumIndex + midIndex;
    }
    }

  937. I used haskell, and this is my implementation just after I first time (successfully) run ‘letsGo’ test. I did some debugging runs on ‘test’ and ‘sanity’ function, though. The whole process from start to quickCheck saying i’m fine took 21 minutes.

    import Test.QuickCheck
    
    bsearch array decide = go (hi + 1) lo
      where
        go lo hi 
          | hi - lo == 1 = array ! lo
        go lo hi = case decide value of
            LT -> go lo center
            EQ -> value
            GT -> go center hi
          where
            value  = array ! center
            center = mid lo hi
    
        (lo, hi) = bounds array
        mid lo hi = (lo + hi) `div` 2
    
    test search = quickCheck $ \mid dev i ->
        let dev'  = abs dev + 1
            lo    = abs mid - dev'
            hi    = abs mid + dev'
            array = listArray (lo, hi) [lo .. hi]
            value = lo + (abs i `mod` dev')
        in  (array :: Array Int Int) `search` value == value
    
    sanity = test (!)
    
    letsGo = test (\arr i -> arr `bsearch` (`compare` i))
    
  938. Pingback: Common bugs and why exercises matter (binary search part 2) | The Reinvigorated Programmer

  939. Pingback: Testing is not a substitute for thinking (binary search part 3) | The Reinvigorated Programmer

  940. Pingback: Writing correct code, part 1: invariants (binary search part 4a) | The Reinvigorated Programmer

  941. Pingback: Writing correct code, part 2: bound functions (binary search part 4b) | The Reinvigorated Programmer

  942. Pingback: Writing correct code, part 3: preconditions and postconditions (binary search part 4c) | The Reinvigorated Programmer

  943. This is the working version… The first failed when the number being searched was also the smallest in the list.

    
    WSH.Echo('hey buddy! doing BS... (code completed in one hour)');
    
    
    function sortNumber(a,b) {
        return a - b;
    }
    
    function getMiddleIndex(firstIndex, lastIndex) {
    	var middleIndex = firstIndex + Math.ceil((lastIndex - firstIndex) / 2);
    	
    	return middleIndex; //missed this line so test failed the first time around!!!!
    }
    
    
    function binaySearch(targetNumber, firstIndex, lastIndex, numberList) {
    	var  middleIndex;
    	
    	WSH.Echo('first: ' + numberList[firstIndex] + '; last: ' + numberList[lastIndex]);
    	
    	middleIndex = getMiddleIndex(firstIndex, lastIndex);
    	
    	if(numberList[middleIndex] == targetNumber){ //match
    		return true;
    	}
    	
    	if(middleIndex == lastIndex ){ //end of search
    		middleIndex = firstIndex;
    		
    		if(numberList[middleIndex] == targetNumber){ //match
    			return true;
    		}
    		
    		return false;
    	}
    	
    	if(numberList[middleIndex] < targetNumber) {
    		return binaySearch(targetNumber, middleIndex, lastIndex, numberList);
    	}
    	else {
    		return binaySearch(targetNumber, firstIndex, middleIndex, numberList);
    	}
    
    }
    
    var numberList = [140, 1000, 104, 19, 90, 20, 13, 141, 200, 1, 56, 98, 14, 22, 200, 12];
    var targetNumber = 56;
    
    numberList.sort(sortNumber);
    
    var firstIndex = 0;
    var  lastIndex, middleIndex;
    
    lastIndex = numberList.length - 1;
    
    var numberFound = binaySearch(targetNumber, firstIndex, lastIndex, numberList);
    
    WSH.Echo(targetNumber + ' found? : ' + numberFound);
    
    
    
  944. FAIL :-( – – some first thinking errors :

    import numpy as np

    array = np.arange(2000)

    search = 1176

    lowb = 0
    upb = len(array)
    diff = upb-lowb

    i = 0

    while True:
    print(i, lowb, upb)
    i += 1
    compval = int((lowb+upb)/2)
    if search = compval:
    lowb = compval
    if search == lowb:
    print(“found at “, lowb)
    break

  945. Well, andi, at least you get points for honesty!

  946. http://pastebin.com/6GQxx9PR
    Python, five minutes (including some testing with null, odd or even list size (didn’t read the full article at first))

  947. I’m sorry to say that I failed – It was able to correctly find values, but didn’t handle the index properly for recursive calls.

    def binary_search(sorted_list, query, start_pos=0):

    list_len = len(sorted_list)
    mid_pos = int(list_len / 2)
    mid_val = sorted_list[mid_pos]

    if mid_val == query:
    return start_pos + mid_pos
    elif list_len == 1 or list_len == 0:
    return -1
    elif query < mid_val:
    return binary_search(sorted_list[0:mid_pos], query, start_pos=start_pos)
    else:
    return binary_search(sorted_list[mid_pos:], query, start_pos=start_pos+mid_pos)

    # Tests
    my_sorted_list = [1,4,6,8,12,34,55,56,445]
    print(my_sorted_list)
    print(1, binary_search(my_sorted_list, 1))
    print(3, binary_search(my_sorted_list, 3))
    print(4, binary_search(my_sorted_list, 4))
    print(6, binary_search(my_sorted_list, 6))
    print(8, binary_search(my_sorted_list, 8))
    print(34, binary_search(my_sorted_list, 34))
    print(55, binary_search(my_sorted_list, 55))
    print(89, binary_search(my_sorted_list, 89))
    print(100, binary_search(my_sorted_list, 100))
    print(445, binary_search(my_sorted_list, 445))

  948. Here’s my version, in C.

    int bin_search(int search, int sorted[], int size)
    {
    int low = 0;
    int high = size;
    while(low<high) {
    int mid = (low+high-1)/2;
    if (searchsorted[mid]) {
    low = mid + 1;
    } else {
    return mid;
    }
    }
    return -1;
    }

    Per the requirements, I have not yet tested it, but I am 95% certain that is correct, and should handle every case, including finding the first and last entries, as well as reporting when the entry does not exist. It took about 60 seconds to write, and has taken much more time for me to just type this response in.

  949. David Starner

    Mark Tarrabain, since low only gets set to 0 or (low + high – 1) / 2 + 1, for, say, high = 10, there’s obviously no way mid could ever equal 1 and thus no way for the function to return 1 if the search element is in spot 1.

  950. Late to the game and probably a not very graceful recursive solution in JavaScript.

    const bsearch = (haystack, needle) => {
      var middle = Math.floor(haystack.length / 2),
        middleElement = haystack[middle];
    
      if (haystack.length === 1) {
        if (haystack[0] === needle) {
          return haystack;
        } else {
          return [];
        }
      } else if (haystack.length === 0) {
        return [];
      }
    
      if (needle < middleElement) {
        return bsearch(haystack.slice(0, middle), needle);
      } else if (needle >= middleElement) {
        return bsearch(haystack.slice(middle), needle);
      } else {
        return [];
      }
    }
    

    “Worked” first time, provided there are no bugs!

  951. Since the comment thread isn’t dead: My (hopefully) correct version passed my tests with a bunch of generated random examples the first time. Written in about 30 min.

    Language: Kotlin
    Recursive implementation
    Bonus: returns (-(insertion point) – 1) when missing (as Java’s Arrays.binarySearch)

    fun binarySearch(list: List, number: Int, startpos: Int = 0): Int {
    val middlepos = list.size / 2
    val middlenumber = list.get(middlepos)
    if (middlenumber == number) {
    return startpos + middlepos
    } else if (middlenumber > number) {
    val left = list.subList(0, middlepos)
    if (left.size == 0) {
    return -startpos – 1;
    }
    return binarySearch(left, number, startpos)
    }
    // else: middlenumber < number
    val right = list.subList(middlepos + 1, list.size)
    if (right.size == 0) {
    return -(startpos + middlepos + 1) – 1
    }
    return binarySearch(right, number, startpos + middlepos + 1)
    }

  952. Old comment threads never die; they only fade away.

  953. int binary(int searchitem,int list[],int size){
    int first=0;last=size-1;
    for(int mid=(first+last)/2;first<=last;mid=(first+last)/2)
    if(searchitemlist[mid])
    first=mid+1;
    else if(searchitem==list[mid])
    return mid;
    else return -1;
    }

  954. I commend you for your honesty.

  955. I’m super late to the party, but not a programmer by trade. I’m an operations/IT guy who codes various things for automation.

    However, I decided to take this on, and knocked it out in VB.NET. I will admit I tested my code a bit. My first version worked, but I was using an array of numbers in sequence. So I later converted that to random numbers. I can find the result in an array of 1 million sorted numbers in typically under 20 loops. Not sure how good it is, but perhaps I took a slightly different approach due to my non-math/non-programmer background.

    This is just a VB.NET Visual Studio 2013 Console Application (yes, its a bit long, but I heavily commented it for folks)

    Dim BaseSortedList As New List(Of Long)
    Dim T As Double = 0

    Sub CreateList()
    ‘A number generator to population my list
    Randomize(Timer)
    Dim R As New Random

    ‘Frist, create my list to sort
    For I As Integer = 1 To 1000000
    BaseSortedList.Add(R.Next(1000000))
    Next
    BaseSortedList.Sort()

    T = BaseSortedList(R.Next(1, BaseSortedList.Count – 1)) ‘I made up some number here I’ll be looking for
    End Sub

    Sub Main()
    CreateList() ‘Just to split up the code

    ‘Now loop until I find it
    Dim Found As Integer = 0 ‘I use an integer so 1=Found, 2=Not in the array at all
    Dim TotalLoops As Integer = 0 ‘Just a counter so I can see how many times I looped through this list
    Dim Rng As Integer = BaseSortedList.Count / 4 ‘This is my range of searching

    Dim CurrentPoint As Long = BaseSortedList.Count / 2 ‘Start in the middle, because shouldn’t you?
    Do
    If T BaseSortedList(CurrentPoint) Then ‘Must be above CurrentPoint
    CurrentPoint += Rng
    If CurrentPoint > BaseSortedList.Count – 1 Then CurrentPoint = BaseSortedList.Count – 1
    End If
    Rng = Rng / 2

    If Rng 0 Then Exit Do
    Loop

    If Found = 1 Then Debug.Print(“Found at ” & CurrentPoint & ” in ” & TotalLoops & ” total loops.”)
    If Found = 2 Then Debug.Print(“Value was *NOT* Found” & ” in ” & TotalLoops & ” total loops.”)
    End Sub

  956. Very late to the party too. I got a CS degree 20 years ago and have been working as a programmer since. I gave this a go, first as a recursive function, then abandoning that and writing a loop with `front` and `back` variables — very similar to what most commenters ended up doing.

    Took 20-30 minutes until I decided to move on to testing. Perhaps that was a bit hurried, as the bugs I found were ones I could have spotted by running the test scenarios in my head. There were two small but important bugs — a `>=` that should be a `<=`, and a conditional clause to handle an empty list.

    Fairly satisfied with this — in the real world you do test (actually, I'm surprised I did as well as I did without test-first methodology)

  957. Thanks, John. Indeed you do test in the real world, but see this followup post on why it’s still important to be able to code without tests as a crutch.

  958. Here is a python solution. Passes all of the tests so far! Wall time was most of a day, but coding time was perhaps 20 minutes…

    def binary_search(L, target):
    first = 0
    last = len(L)
    while first < last:
    middle = (first + last)/2 ### first – 1 < middle target:
    last = middle
    elif L[middle] < target:
    first = middle + 1
    else: ### assume trichotomy, so L[middle] == target:
    return middle
    return None

  959. Here’s a Python implem. I’m pretty sure it works.

    {source}
    def binary_search_rec(element, array, left, right):
    if left == right – 1:
    return left if array[left] == element else -1
    pivot = (left + right) // 2
    if array[pivot] > element:
    return binary_search_rec(element, array, left, pivot)
    return binary_search_rec(element, array, pivot, right)

    def binary_search(element, array):
    if not array:
    return -1
    return binary_search_rec(element, array, 0, len(array))
    {/source}

  960. C++ template version with iterators:

    template
    RandomIt bsearch(RandomIt beg, RandomIt end, T const& value)
    {
    while (beg != end) {
    auto middle = beg + (end - beg)/2;
    if (*middle < value)
    beg = ++middle;
    else if (value < *middle)
    end = middle;
    else
    return middle;
    }

    return beg;
    }

    I am not sure if I should count that as a success or as a failure, because I didn’t write `else` before `return middle;`, and did not notice until I actually ran the code (when I tried to mentally replay the code, I acted as though `else` was there).

  961. hededegoddamit

    Actually, `return middle` isn’t even necessary. Well, depends on case, if you want to return past-the-end iterator (`auto not_found = end; while(…) {} return not_found;`), it is needed.

  962. I have made mistake, in call of recursion

  963. Guaranteed success. Written in comment box in one pass, not tested
    // java
    public int bSearch(int[] nums, int target) {
    // return index of first occurrence of target in nums
    if (nums == null || nums.length == 0) return -1;
    int start = 0;
    int end = nums.length – 1;
    while (start + 1 = target) {
    end = mid;
    } else {
    start = mid;
    }
    }
    if (nums[start] == target) return start;
    return end;
    }

  964. In Haxe: https://gist.github.com/binki/fdaa9d928143a1d68cb7ab2f94af9acd , supposedly my implementation was correct when I wrote it, got distracted, re-looked at it and fixed stuff, and then finally tested it. However, when I wrote my cmp() function (my bsearch() implementation takes a comperer as an argument), I got confused and wrote it to return the opposite values as I had documented that my bsearch() accepted. So after fixing my cmp(), my tests passed.

  965. Well done! It’s amazing how easy it is to let a trivial mistake like that slip past.

  966. Here it is in common lisp. I haven’t tested it yet, apart from making sure that it compiles. b-search is an entry point. It only searches in lists of strings. Obviously, it’s gonna fail badly on things like passing circular list in, passing string instead of list, or having elements which are not strings, etc. But I assume we’re talking about algorithm, not our input sanitizing skills.

    Honestly, I’ll be surprised if it works, there are lot of places for fence errors.

    {source}
    (defun b-search (list elem)
    (let* ((list-length (length list))
    (arr (sort (make-array list-length
    :initial-contents
    list)
    ‘string<)))
    (b-search-array arr
    elem
    0
    (1- (length arr)))))

    (defun b-search-array (arr elem start end)
    (let ((length (1+ (- end start))))
    (cond ((<= length 0) nil)
    ((= length 1) (aref arr start))
    (t (let* ((middle (+ start (floor (/ length 2))))
    (middle-element (aref arr middle)))
    (if (string< elem middle-element)
    (b-search-array arr elem start (1- middle))
    (b-search-array arr elem middle end)))))))

    {source}

  967. Good on you for submitting before testing! So — have you tested now, as well? How did it go?

  968. First attempt:

    int binary_search(std::vector<int>  & int_array, int start, int end, int find)
    {
    	if (start > end)
    		return -1;
    	int mid = (start + end) / 2;
    	if (int_array[mid] == find)
    		return mid;
    	else if (int_array[mid] < find)
    		return binary_search(int_array, mid + 1, end, find);
    	else
    		return binary_search(int_array, start, mid - 1, find);
    }
    

    This ignored the case when int_array was empty, resulting in an attempt to index an empty vector which obviously led to an error (sidenote: I don’t think assuming that the passed vector is non-empty is an unreasonable assumption ..).

    Fixed version:

    int binary_search(std::vector<int>  & int_array, int start, int end, int find)
    {
    	if (start > end || int_array.size() == 0)
    		return -1;
    	int mid = (start + end) / 2;
    	if (int_array[mid] == find)
    		return mid;
    	else if (int_array[mid] < find)
    		return binary_search(int_array, mid + 1, end, find);
    	else
    		return binary_search(int_array, start, mid - 1, find);
    }
    

    This one worked fine on Steve’s 4096 test cases, which I assume are comprehensive and test all edge-cases.

  969. Pingback: Solving Scramble With Friends – a tale of three data structures | Developmentality

  970. Pingback: Why the element buffer of ArrayList in java is not reduced in size when elements are removed from ArrayList? – Best Java Answer

  971. Kalman Reti

    I failed. It took me ten minutes to write, but the first version used ‘midpoint’ instead of the (at the time nonexistent) ‘index’, with hilarious results. The fix took only a few seconds, namely adding the ‘for index’ clause and replacing the previous uses of ‘midpoint’ with ‘index’. This function is a little more complicated than the loop expressions I routinely type into emacs to analyze things in buffers, but not by much. (Most of those work first try.)

    I’m posting the fixed elisp/common lisp code [it is the same in both, which isn’t always true], because although there are several prior versions in lisp, I saw none using the loop macro. (I confess I didn’t read all, but I did search through the comments for ‘lisp’ and read those so marked.)

    In my opinion, the loop macro version is much more readable than the other, usually recursive, lisp versions (even though it is longer). It is also more conducive to getting right the first time because 1) you can break a problem down into byte-sized independent clauses computing the elements you need and 2) you can name the variables in those clauses so that they clearly indicate what they hold.

    Indeed, it was a misnaming that led to my original bug; a better name for ‘midpoint’ would have been ‘range-midpoint’, which, had I originally thought to use it, would have made it less likely for me to have confused it with the concept that ‘index’ is in the fixed version.

    (defun number-binary-search (value array)
      "returns index into sorted array of numbers that holds value, or nil if none"
        (loop with l = (length array) and answer and new-lower and new-limit
    	  until answer
    	  for lower = 0 then new-lower
    	  for limit = l then new-limit
    	  for range = (- limit lower)
    	  when (zerop range)
    	  return nil
    	  for midpoint = (floor range 2)
    	  for index = (+ lower midpoint)
    	  for probe-value = (aref array index)
    	  if (eql probe-value value)
    	  do (setq answer index)
    	  else
    	  if (< probe-value value)
    	  do (setq new-lower (1+ index)
    		   new-limit limit)
    	  else
    	  do (setq new-lower lower
    		   new-limit index)
    	  finally (return answer)))
    
    (binary-search 3 [0 0 0 0 0 0 0 1 2 3]) => 9
    
  972. I wrote it in Python and tested it for a couple 1000 randomly generated test cases. It seems fine. Took not very long. I am writing this because that is THE RULE.

  973. I wasn’t sure if I should return true/false or index/-1. I blame the specs. My code ended up returning true/false but I’m fairly confident I could modify it to return index/-1.
    Ruby:
    {source}
    def bin_search(arr,val)
    case arr.size
    when 0
    return false
    when 1
    return arr[0]==val
    when 2
    return arr[0]==val || arr[1]==val
    else
    half_index = arr.size/2
    case
    when arr[half_index]==val
    return true
    when arr[half_index]>val
    return bin_search( arr[0…half_index], val )
    when arr[half_index]<val
    return bin_search( arr[half_index+1..-1], val )
    end
    end
    end
    {/source}
    One of the things I was going to do was to throw an exception if val was not comparator-able, but I forgot.
    The code appears to be bug-free, it runs correctly and quickly with arrays of integers, and, most importantly, it makes me happy to look at.
    Time: 5 minutes.

  974. Update: RIP did not read specs and used curly braces.

    def bin_search(arr,val)
    	case arr.size
    	when 0
    		return false
    	when 1
    		return arr[0]==val
    	when 2
    		return arr[0]==val || arr[1]==val
    	else
    		half_index = arr.size/2
    		case
    		when arr[half_index]==val
    			return true
    		when arr[half_index]>val
    			return bin_search( arr[0...half_index], val )
    		when arr[half_index]<val
    			return bin_search( arr[half_index+1..-1], val )
    		end
    	end
    end
    
  975. Update: Moved it from true/false to index/-1

    def bin_search(arr,val,offset=0)
    	case arr.size
    	when 0
    		return -1
    	when 1
    		return offset if arr[0]==val
    		return -1
    	when 2
    		return offset if arr[0]==val
    		return offset+1 if arr[1]==val
    		return -1
    	else
    		half_index = arr.size/2
    		case
    		when arr[half_index]==val
    			return offset+half_index
    		when arr[half_index]>val
    			return bin_search( arr[0...half_index], val, offset )
    		when arr[half_index]<val
    			return bin_search( arr[half_index+1..-1], val, offset+half_index+1 )
    		end
    	end
    end
    
  976. Exceptions for incorrectly typed arguments would be an irrelevance here: this is a challenge on algorithms, not defensive engineering. Well done on getting working code … though I would worry a bit about the plethora of special cases for arrays of size 0, 1 and 2.

  977. @Mike Taylor
    Thanks for responding.
    The code is recursive and I’m passing the search zone each time. I figured that it’d be better to include cases than to run an extra iteration. The code should still run correctly if I omit the tests for size 1 and 2 (though the 0-size test is necessary).
    The real importance of those test cases is that they show my thinking process. I wrote this code top to bottom, so I had already figured out the way I wanted to handle recursion by the second line. That way was, upon review, not the cleanest way, and so in the future, I’ll review my drafts before submitting the final product.
    The series as a whole was very helpful, as I’m still learning the thinking processes behind designing algorithms. Thank you for providing such detailed explanations on those.

  978. Thank you, it’s great to know these posts are helpful!

    But don’t you feel the version at https://reprog.wordpress.com/2010/04/25/writing-correct-code-part-1-invariants-binary-search-part-4a/ is more elegant (or, at least, would be if translated into Ruby)?

  979. I didn’t tested it yet (wanted to post it before, i just run the “compiler/syntax check”) It took me around 30-40 minutes. And it is done in Python2

    def bin_search(sorted_list, value):
        res_id = -1
        if sorted_list:
            min_id = 0
            max_id = len(sorted_list)-1
            while min_id < max_id:
                cur_id = (max_id+min_id)/2
                if sorted_list[cur_id] == value:
                    res_id = cur_id
                    break
                elif sorted_list[cur_id] < value:
                    min_id = cur_id + 1
                else:
                    max_id = cur_id - 1
            if min_id == max_id and sorted_list[min_id] == value:
                res_id = min_id
        return res_id
    
    
    def test(compile_only=True):
        t_data = [(-1, [1,2,3,4,5], -1),
                (6,  [1,2,3,4,5], -1),
                (5,  [0,2,7,8,9], -1),
                (5,  [0,1,  2,8], -1),
                (4,  [0,1,4,5  ],  2),
                (4,  [4,5,6,7  ],  0),
                (1,  [1,2,3,4,5],  0),
                (5,  [1,2,3,4,5],  4),
                (1,  [         ],  -1)]
        for t_value, t_list, t_res in t_data:
            res = bin_search(t_list, t_data)
            if not compile_only:
                assert (t_res == res), "Waiting for %d, but got %d -_- " % (t_res, res)
        
        print "Everything is fine!!! ^_^"
    
    if __name__ == "__main__":
        test()
  980. Good luck! I particularly appreciate the chutzpah of including the line “Everything is fine!!! ^_^” in code that you have not yet run, and I hope it works out :-)

  981. The test is a succes for me (The previous version with Python2),
    altough there is a bug in… the test Function :D , instead of bin_serch(t_list, t_data) it should be bin_search(t_list, t_value).

    Hopefully i can still pretend to be part of the 10% ^_^ (Assuming my tests cover every possibility)

  982. Congratulations, Mario, I think that qualifies. If this was being done as a formal competition, then your binary search function would run in a test harness provided by the organisers, so getting that part of the code right would not be your responsibility.

    It’s funny how often people’s tests contain bugs, though — see Testing is not a substitute for thinking for more thoughts on this.

  983. In Erlang:

    -module(binary_search).
    
    -export([find/2]).
    
    find(List, Value) ->
            find(List, Value, 0).
    
    find([], _, _) -> undefined;
    find(L, V, Offset) ->
            SplitAt=length(L) div 2,
            case lists:split(SplitAt, L) of
                    {_, [E|_]} when E=:=V -> Offset+SplitAt;
                    {Left, [E|_]} when E>V -> find(Left, V, Offset);
                    {_, [_|Right]} -> find(Right, V, Offset+SplitAt+1)
            end.
    
  984. Thanks, Uhu — I think that this is the first Erlang submission!

    Did it work?

  985. Fib search is faster!

  986. In some circumstances, maybe — though less likely these days than when multiplication was expensive. But the key point is that it’s much, much harder to get right.

  987. Hey only nine or so year late! I’m not a pro so I broke all your rules. Here is my submission written in Livecode.

    private Function BinarySearch  @pArray, pItem, pRight
       -- Livecode
       put 0 into tLeft
       put pRight into tRight
       
       Repeat while tLeft <= tRight
          
          put floor ((tLeft+tRight)/2) into tMidpoint
          
          if pArray[tMidpoint]["FileName"] < pItem then
             put tMidpoint + 1 into tLeft
          else if pArray[tMidpoint]["FileName"] > pItem then
             put tMidpoint - 1 into tRight
          else
             return true
          end if
          
       end Repeat
       
       Return false  -- not found
    end BinarySearch
    
  988. Pingback: The Best CS Books | CodersCat

  989. Pingback: The Best CS Books – Hacker's Wisdom

  990. Not tested yet. I’ve also not looked at any of the other comments. The specification is somewhat vague about exactly what the interface should be (what are the things being looked up, what to do if there’s more than one matching entry, and so on).

    Possibly unusually, I’ve opted for a strict one-sided comparison (Common Lisp convention), rather than the 3-way comparison assumed by, say, bsearch(3), and I work throughout using a half-open interval. I have been careful about index arithmetic overflow.

    /* -*-c-*- */
    
    #include <stddef.h>
    
    struct key { int x; };
    
    struct entry { struct key k; };
    
    static int lessp(const struct key *a, const struct key *b)
      { return (a->x < b->x); }
    
    struct entry *binary_search(const struct key *k,
    			    const struct entry *v, size_t n)
    {
      size_t lo = 0, hi = n;
      size_t mid;
    
      /* Some special cases. */
      if (!n) return (0);
      if (lessp(k, &v[0].k)) return (0);
    
      for (;;) {
        /* Invariants: lo < hi; v[lo].k <= k < v[hi.k], with the convention that
         * v[n].k is infinitely large.
         */
    
        /* Pick a point in between. */
        mid = lo + (hi - lo)/2;
    
        /* Check for termination. */
        if (mid == lo) {
          /* There's only one spot left; otherwise hi - lo >= 2, so (hi - lo)/2
           * >= 1 and mid > lo.  We know that v[lo].k <= k.  If v[lo].k < k then
           * we fail; otherwise trichotomy tells us that we've found the right
           * one.  (Or one of them, if there are several.)
           */
    
          if (lessp(&v[lo].k, k)) return (0);
          else return ((/*unconst*/ struct entry *)&v[lo]);
        }
    
        /* Compare against the midpoint we chose and narrow the interval. */
        if (lessp(k, &v[mid].k)) hi = mid;
        else lo = mid;
      }
    }
    
  991. Wow. I think I might have gotten that right. Also, chalk me up as someone else who mentioned `invariants’ :-). Tested by hand, and against Witham’s suite.

    The test driver I wrote was this:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
      struct key k;
      struct entry *v, *e;
      size_t i, n;
    
      if (argc < 2)
        { fprintf(stderr, "usage: %s KEY [ITEM ...]\n", argv[0]); exit(2); }
      k.x = atoi(argv[1]);
      n = argc - 2;
      if (!n)
        v = 0;
      else {
        v = malloc(n*sizeof(*v));
        if (!v) { fprintf(stderr, "%s: out of memory\n", argv[0]); exit(2); }
        for (i = 0; i < n; i++) v[i].k.x = atoi(argv[i + 2]);
      }
      e = binary_search(&k, v, n);
      if (!e) { fprintf(stderr, "%s: not found\n", argv[0]); exit(1); }
      else { printf("found %d\n", e->k.x); exit(0); }
    }
    

    and the machinery I wrote to consume Witham’s test data is the following zsh, which was a one-liner until I formatted for this comment:

    zcat tests.txt.gz | while read p n; do
      case $p in Problem) ;; *) echo >&2 "ouch"; break;; esac
      read k
      read _
      v=
      while :; do
        read x y
        case $x in "]?") break ;; esac
        v="$v $x"
      done
      out=$(./search $k ${=v} 2>/dev/null); rc=$?
      set -- ${=out}
      case $rc,$y,$2 in
        0,yes,$k) ;;
        1,no,*) ;;
        *) echo >&2 "$n: wanted $y; found $2 (rc = $rc)"; break ;;
      esac
      read _
    done
    
  992. Here’s my python3 implementation. It worked on the first try! Can probably be optimized though…

    def binary_search(l, target, start=0):
        if len(l) == 0:
            return False
        if len(l) == 1:
            return start if l[0] == target else False
    
        length = len(l)
        mid = length // 2
        check = l[mid]
    
        if check == target:
            return start + mid
        elif check > target:
            new_list = l[:mid]
            return binary_search(new_list, target, start)
        elif check < target:
            offset = mid+1
            new_list = l[offset:]
            return binary_search(new_list, target, start+offset)
    
  993. On top of my fails-on-empty-list problem, it appears I misunderstood the assignment. I wrote my program to find the full range of indices where the values match, rather than a single index. So that’s a double-failure for me.

  994. Interesting how easy it is to make this kind of mistake!

  995. Howdy, already did this once, wanted to try with my latest obsession: ladder logic. My cheats are as follows:

    1) Made an entry a few years ago in C.

    2) Wrote and tested a linear algorithm first, so I’d have something to test the binary search. It took me a long time to get the linear algorithm right, especially the anomalous case handling (so as far as I am concerned I am already in the 90% ;-).

    Other than that, the commit 71cab87 here [https://github.com/drbitboy/Ladder_10percent/commit/71cab8776988b6dac93e0841c76003380a4a33ce] under [https://github.com/drbitboy/Ladder_10percent] is fresh and untested, other than by eye.

    My test cases use zero to eight elements from the start of the array [1,2,2,3,5,6,6,6], with eight integral sought values from 0 to 7, so 72 cases.

  996. So close ;-/.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.