---
title: Eboard 35  Binary search
number: 35
section: eboards
held: 2017-11-20
---
CSC 151.01, Class 35:  Binary search
====================================

_Overview_

* Preliminaries
    * Notes and news
    * Upcoming work
    * Extra credit
    * Questions
* Divide and conquer
* Destructive binary search (a demonstration)
* Binary search in Scheme
* Lab
* Debrief

### News / Etc.

* Quiz 12 returned.
* I expect that the next set of grades you get from me will be after
  Thanksgiving.

### Upcoming Work

* [Writeup for class 35](../writeups/writeup35) due Wednesday at 10:30 p.m.
    * Exercise 3
    * To: <csc151-01-grader@grinnell.edu>
    * Subject: CSC 151.01 Writeup 35 (YOUR NAMES)
* Project proposals due TONIGHT.
* Projects due next Tuesday.
* No reading for Wednesday.
* No quiz this week.

### Extra credit (Academic/Artistic)

* Tuesday community hour panel on guns.
* CS table tomorrow: Uber and "the gig economy".

### Extra credit (Peer)

### Extra credit (Misc)

### Other good things

* Harp recital Tuesday night.

### Questions

Divide and conquer
------------------

Traditionally, we search lists with *linear search* (as in association
list).  We look at element 0.  If it matches, we're done.  We look at
element 1.  If it matches, we're done.  We look at element 2.  If it
matches, we're done.  We look at element 3.  If it matches, we're done.
And so on and so forth.

If a list has n elements, we potentially look at all of them.  In the
worst case (it's not there), we do look at all of them.

Can we do better?

If we can arrange the elements in order from smallest to largest, in a
vector, we can look in the middle.  (Or we run out of elements.)

* The middle element can match.  We're done.  
* The middle element can be too big.  We look in the stuff before it.
* The middle element can be too small.  We look in the stuff after it.

Destructive binary search (a demonstration)
-------------------------------------------

How many names did I have to look at?

1600 -> 800 -> 400 -> 200 -> 100 -> 50 -> 25 -> 12 -> 6 -> 3 -> 1 -> Done

It tooks us about eleven recursive calls to find a person out of 1600.

If we had 4,000,000 (Manhattan, Iowa).  About 22 steps.

Binary search is a great algorithm, but has restrictions that

* The data are stored in a vector.  And use the vector cleverly.
* The data must be arranged from smallest to largest.

Binary search in Scheme
-----------------------

_Explain each of the four parameters to binary search._

```
(define binary-search
  (lambda (vec get-key may-precede? key)
```

* `vec` is a vector.
* `key` is a value, which represents the thing we want.
* `get-key` is a procedure which extracts the key.
     * What is "the key"?  The important part of each entry.
       Like in assoc, where the first element of each list is the key.  
     * The generalized "tell us what's important"
* `may-precede?` tells us what can come before something else.
     * If we organize the vector using strings, it's `string-ci<=?` or
       `string=?`
     * If the keys are integers, it's <=.

In the following vector of people with sidekicks arranged by protagonist, 
the car is the key

```
(define sidekicks
  (vector 
   ("Batman" "Robin")
   ("Halle" "Marli")
   ("Scooby" "Shaggy")
   ("Superman" "Krypto")
   ("Yogi" "Booboo")
  ))
```

To search that vector for Secret Squirrel's sidekick, I'd use
`(binary-search sidekicks car string-ci<=? "Secret Squirrel")`

```
(define sidekicks-by-sidekick
  (vector 
   ("Yogi" "Booboo")
   ("Halle" "Marli")
   ("Superman" "Krypto")
   ("Batman" "Robin")
   ("Scooby" "Shaggy")
  ))
```

To find out Robin's protagonist, I might use something like
`(binary-serach sidekicks-by-sidekick cadr string-ci<=? "Robin")`

We can arrange the elements however we want.  I'll generally use lists
for each element, but you could also use a vector or a tree or ....

_What does binary search return?_

It returns the index of the matching element.  It returns -1 if it
doesn't match.

_How do we apply the "divide and conquer" technique without building
new vectors (which is computationally expensive)?_

Instead of literally throwing away half the vector, we keep track of 
the start and end of the portion still of interest.  You can find
the midpoint and refine your knowledge of the start and end of that
portion.

Lab
---

Observation: Since the way we decide that it's a match is to check
whether the desired key may precede the middle key AND the middle key
may precede the desired key, we need to use `<=` rather than `<` as
`may-precede?`.

Observation: If the same key appears multiple times, it is difficult
to predict which instance is chosen.  (It is also not consistent.)
That is a consequence of how binary search works; it's whichever one
appears in the middle of the range of interest.

For exercise 5, I mean `caddr`, not `(r-s list-ref 5)`.

Writeup: Exercise 3

Debrief
-------

For exercise 4: What happens right before the element is not found?

* Option 1: `lower-bound` = `upper-bound` and the middle element is
  too large.  We now have `upper-bound` = `lower-bound`-1.
* Option 2: `lower-bound` = `upper-bound` and the middle element is
  too small.  We now have `lower-bound` = `upper-bound`+1.
* Option 3: `lower-bound` = `upper-bound`-1 (which means that the
  middle element is at `lower-bound`) and the middle element is
  too large.  We now have `upper-bound` = `lower-bound`-1.
* Option 4: `lower-bound` = `upper-bound`-1 (which means that the
  middle element is at `lower-bound`) and the middle element is too
  small.  We now have `lower-bound` = `upper-bound` and we're in
  one of the three other options.
