Binary Search

Calvin (Deutschbein)

W13Fri: 22 Nov

Announcements

  • Special out-of-sequence lecture Monday.
  • Adventure Ongoing
    • You should be thinking about how you're going to do QUIT/HELP/LOOK
  • 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
        • the problem is not urgent and I'll solve it latter, or
        • I am fleeing a zamboni IRL and require thoughts & prayers stat.

Today

  • Do things fast
    • Not adventure relevant
    • Is write-good-code relevant.
    • Searchin'

Throwback Wordle

  • Read in some text (hey just like adventure)
  • See if it's in a list of words (just like adventure)
  • 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:str)->bool: 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 make peanut butter.
  • It is more algorithmically correct - it wastes less effort.

Checking Words

  • One peanut at a time: def is_my_word_english(my_word:str)->bool: 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:str, word_list:list[str]) -> bool: 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:str, word_list:list[str]) -> bool: 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) > 2: ... return word_list == word_list[0] # only one word left!

Divide and Conquer

  • Altogether def is_word(my_word:str, word_list:list[str]) -> bool: 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 in two weeks.

Today

  • Do things fast
    • Not adventure relevant
    • Is write-good-code relevant.
    • Searchin'

Announcements

  • Special out-of-sequence lecture Monday.
    • Alonzo Church's Lambda Calculus 🙏
    • Adventure Ongoing
      • You should be thinking about how you're going to do QUIT/HELP/LOOK
    • 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
          • the problem is not urgent and I'll solve it latter, or
          • I am fleeing a zamboni IRL and require thoughts & prayers stat.