Binary Search
Calvin (Deutschbein)
W13Fri: 21 Nov
Announcements
- Adventure Ongoing
- You should be thinking about how to navigate between scenes
- Advising ongoing
- If you encounter any problem, email me immediately
- I'll be doing triage:
- If you don't get an email back quickly, either
- I'm on a multi-hour trail run and/or asleep, or
- I will be able to solve any problems you encounter non-urgently.
- Either way, once you send the email it is not your problem.
Today
- Do things fast
- Not adventure relevant
- Is write-good-code relevant.
- Searchin'
Throwback Wordle
- Read in some text (hey just like wordle)
- See if it's in a list of words (just like wordle)
- Hella words (this is where it's different)
- Do thing accordingly.
- Wordle's ENGLISH_WORDS had 127145 words
- It's here.
- That would take a long time to read.
Checking Words
- The obvious way to see if a word is in a list is with a loop:
def is_my_word_english(my_word):
for word in ENGLISH_WORDS:
if word == my_word:
return True
return False
- This is technically correct.
- It is a special new type of wrong: algorithmically incorrect.
Technical vs Algorithmic Correctness
- Say I want to make a peanut butter sandwich
- This is an easy assumption as it is true at all points in time
- I could construct a peanut butter sandwich by:
- Driving to Winco Foods or equivalent
- Purchasing one peanut
- Driving to Lowe's Home Improvement or equivalent
- Buying a grinder
- Driving home
- Grinding one peanut
- Storing the peanut butter
- Returning the grinder
- Looping.
- This actually how I lived my life before discovering list comprehensions, now I buy 100 grinders at a time. (This is a joke it is not serious).
Technical vs Algorithmic Correctness
- Say I want to make a peanut butter sandwich
- I have 20 metric tons of peanuts and 40 grinders.
- I divide the tons of peanuts into equal parts until I have 40 parts
- I grind them all at once.
- This technique of "divide-and-conquer" is just as technically correct - it still makes peanut butter.
- It is more algorithmically correct - it wastes less effort (driving, in this case)
Checking Words
- One peanut at a time:
def is_my_word_english(my_word):
for peanut in ENGLISH_WORDS:
if peanut == my_word:
return True
return False
- This is technically correct.
- Is there a way to divide it in half?
Divide and Conquer
- We can think of words alphabetically, and check to see if, e.g.:
- The first letter of a word is in the first half of the alphabet
>>> abcs = 'abcdefghijklmnopqrstuvwxyz'
>>> abcs[len(abcs)//2]
'n'
>>> "vesuvius" < 'n'
False
>>> "glissando" < 'n'
True
Divide and Conquer
- We can think of words alphabetically, and check to see if, e.g.:
- If words are in alphabetical order, it is easy enough to see if a word is in the first half of the words:
>>> words = ['abraxis', 'canopy', 'palladio', 'snafu', 'turbidity', 'unceasingly', 'zymox']
>>> words[len(words)//2]
'snafu'
>>> "vesuvius" < 'snafu'
False
>>> "glissando" < 'snafu'
True
Divide and Conquer
- How did this work algorithimically?
- We took one word and a sorted list of words.
- We checked if our word was more or less than the middle of the sorted list
- Let's write that out:
def is_first_half(my_word, word_list):
length = len(word_list)
half_length = length // 2 # we need an integer!
middle = word_list[middle]
return my_word < middle
- Note I do not use any if statements!
Divide and Conquer
- How did this work algorithimically?
- This only tells us which half a word would be in, not if it is in that half.
def is_first_half(my_word, word_list):
return my_word < word_list[len(word_list) // 2]
- We could check the whole half but...
- Checking everything is just what we want to avoid!
- We can use the same trick again - keep taking halves...
if is_first_half(my_word, word_list):
# keep only the first half
word_list = word_list[:half_length]
else:
# keep only the second half
word_list = word_list[half_length:]
Divide and Conquer
- How did this work algorithimically?
- We can keep talking halves until there is only one word left.
while len(word_list) > 1:
half_length = len(word_list) // 2
if is_first_half(my_word, word_list):
# keep only the first half
word_list = word_list[:half_length]
else:
# keep only the second half
word_list = word_list[half_length:]
- Then we can see if that's the word we want.
while len(word_list) > 1:
...
return word_list == word_list[0] # only one word left!
Divide and Conquer
- Altogether
def is_first_half(my_word, word_list):
while len(word_list) > 1:
half_length = len(word_list) // 2
if my_word < word_list[half_length]:
# keep only the first half
word_list = word_list[:half_length]
else:
# keep only the second half
word_list = word_list[half_length:]
return my_word == word_list[0]
- More Monday.
Today
- Do things fast
- Not adventure relevant
- Is write-good-code relevant.
- Searchin'
Announcements
- Adventure Ongoing
- You should be thinking about how to navigate between scenes
- Advising ongoing
- If you encounter any problem, email me immediately
- I'll be doing triage:
- If you don't get an email back quickly, either
- I'm on a multi-hour trail run and/or asleep, or
- I will be able to solve any problems you encounter non-urgently.
- Either way, once you send the email it is not your problem.