Unable to properly compare Binary vs. Linear search

Question:

def linear_search(a_list, key):
    steps = 0
    for i, item in enumerate(a_list):
        steps += 1
        if item == key:
            break
    return steps


def binary_search(a_list, key):
    a_list.sort()

    steps = 1

    left = 0
    right = len(a_list) - 1
    while left <= right:
        steps += 1
        middle = (left + right) // 2

        if a_list[middle] == key:
            break
        if a_list[middle] > key:
            right = middle - 1
        if a_list[middle] < key:
            left = middle + 1
    return steps


    def best_search(a_list, key):
        steps_linear = linear_search(a_list, key)
        steps_binary = binary_search(a_list, key)
        results = "Linear: " + str(steps_linear) + " steps, "
        results += "Binary: " + str(steps_binary) + " steps. "
        if linear_search(a_list, key) < binary_search(a_list, key):
            results += "Best Search is Linear."
        elif binary_search(a_list, key) < linear_search(a_list, key):
            results += "Best Search is Binary."
        else:
            results += "Result is a Tie."

        return results

Test

    print(best_search([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1))
    print(best_search([10, 2, 9, 1, 7, 5, 3, 4, 6, 8], 1))
    print(best_search([10, 9, 8, 7, 6, 5, 4, 3, 2, 1], 7))
    print(best_search([1, 3, 5, 7, 9, 10, 2, 4, 6, 8], 10))
    print(best_search([5, 1, 8, 2, 4, 10, 7, 6, 3, 9], 11))

I have debugged this code and can’t figure out why it will not tie. It does count the steps right but doesn’t properly decide which one is better.

Asked By: zack_cream

||

Answers:

So there are a couple of problems with your code:

  1. As @Samwise said, you aren’t calling your search functions in best_search. That should be updated:

    steps_linear = linear_search(a_list, key)
    steps_binary = binary_search(a_list, key)
    ...
    
  2. However, once you do that, another problem arises after you run the code:

    Linear: -1 steps, Binary: 0 steps. Best Search is Linear.
    Linear: -1 steps, Binary: 0 steps. Best Search is Linear.
    Linear: -1 steps, Binary: 6 steps. Best Search is Linear.
    Linear: -1 steps, Binary: 9 steps. Best Search is Linear.
    Linear: -1 steps, Binary: -1 steps. Result is a Tie.
    

    These obviously aren’t the desired outputs. Why is linear_search always returning -1? It is always returning -1 because that’s what you specify it to always return:

    def linear_search(a_list, key):
        # Returns the number of steps to determine if key is in the list
        # Initialize the counter of steps
        steps = 0
        for i, item in enumerate(a_list):
            steps += 1
            if item == key:
                break
        return -1 # <--- this is your only return statement!!
    

    But you want to return the number of steps it take to perform the search—so return that. Instead of breaking the loop when (if) you find the key, just return it, since returning the steps will end the function:

    if item == key:
     return steps
    

    Output:

    Linear: 1 steps, Binary: 0 steps. Best Search is Binary.
    Linear: 4 steps, Binary: 0 steps. Best Search is Binary.
    Linear: 4 steps, Binary: 6 steps. Best Search is Linear.
    Linear: 6 steps, Binary: 9 steps. Best Search is Linear.
    Linear: -1 steps, Binary: -1 steps. Result is a Tie.
    

    Already looking better . linear_search returns the desired output for all but the last test, which I’ll get to later.

  3. The function binary_steps, though, isn’t returning the desired outputs. Why? Well, for starters, you never actually return steps:

    def binary_search(a_list, key):
        # Returns the number of steps to determine if key is in the list
        # List must be sorted:
    
        a_list.sort()
    
        # The Sort was 1 step, so initialize the counter of steps to 1
        steps = 1
    
        left = 0
        right = len(a_list) - 1
        while left <= right:
            middle = (left + right) // 2
    
            if a_list[middle] == key:
                return middle # you return `middle`
            if a_list[middle] > key:
                right = middle - 1
            if a_list[middle] < key:
                left = middle + 1
        return -1 # or -1, but not `steps`
    

    Instead, you return middle when a match is found. So replace that with return steps:

    if a_list[middle] == key:
        return steps
    

    But now you get this:

    Linear: 1 steps, Binary: 1 steps. Result is a Tie.
    Linear: 4 steps, Binary: 1 steps. Best Search is Binary.
    Linear: 4 steps, Binary: 1 steps. Best Search is Binary.
    Linear: 6 steps, Binary: 1 steps. Best Search is Binary.
    Linear: -1 steps, Binary: -1 steps. Result is a Tie.
    

    That’s still not your desired output. Why isn’t binary_search returning the desired output? Because you are not incrementing steps for each iteration of the loop, so steps will always be 1! Increment steps at each loop iteration:

    while left <= right:
        steps += 1
        middle = (left + right) // 2
        ...
    

    …and look what we’ve got:

    Linear: 1 steps, Binary: 4 steps. Best Search is Linear.
    Linear: 4 steps, Binary: 4 steps. Result is a Tie.
    Linear: 4 steps, Binary: 5 steps. Best Search is Linear.
    Linear: 6 steps, Binary: 5 steps. Best Search is Binary.
    Linear: -1 steps, Binary: -1 steps. Result is a Tie.
    
  4. Everything but the last test returns your desired output. But what’s up with that last test? Let’s check it out:

    print(best_search([5, 1, 8, 2, 4, 10, 7, 6, 3, 9], 11))
    # Should be: Linear: 10 steps, Binary: 5 steps. Best Search is Binary.
    

    Well, it’s clear to see that you won’t find 11 in a list without 11, so you can never get your desired output here. You’d either have to change the key, or modify the input list and and an 11 to it.

Here’s the final code:

def linear_search(a_list, key):
    # Returns the number of steps to determine if key is in the list
    # Initialize the counter of steps
    steps = 0
    for i, item in enumerate(a_list):
        steps += 1
        if item == key:
            return steps
    return -1


def binary_search(a_list, key):
    # Returns the number of steps to determine if key is in the list
    # List must be sorted:

    a_list.sort()

    # The Sort was 1 step, so initialize the counter of steps to 1
    steps = 1

    left = 0
    right = len(a_list) - 1
    while left <= right:
        steps += 1
        middle = (left + right) // 2

        if a_list[middle] == key:
            return steps
        if a_list[middle] > key:
            right = middle - 1
        if a_list[middle] < key:
            left = middle + 1
    return -1


def best_search(a_list, key):
    steps_linear = linear_search(a_list, key)
    steps_binary = binary_search(a_list, key)
    results = "Linear: " + str(steps_linear) + " steps, "
    results += "Binary: " + str(steps_binary) + " steps. "
    if steps_linear < steps_binary:
        results += "Best Search is Linear."
    elif steps_binary < steps_linear:
        results += "Best Search is Binary."
    else:
        results += "Result is a Tie."

    return results

print(best_search([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1))
# Should be: Linear: 1 steps, Binary: 4 steps. Best Search is Linear.

print(best_search([10, 2, 9, 1, 7, 5, 3, 4, 6, 8], 1))
# Should be: Linear: 4 steps, Binary: 4 steps. Result is a Tie.

print(best_search([10, 9, 8, 7, 6, 5, 4, 3, 2, 1], 7))
# Should be: Linear: 4 steps, Binary: 5 steps. Best Search is Linear.

print(best_search([1, 3, 5, 7, 9, 10, 2, 4, 6, 8], 10))
# Should be: Linear: 6 steps, Binary: 5 steps. Best Search is Binary.

print(best_search([5, 1, 8, 2, 4, 10, 7, 6, 3, 9], 11))
# Should be: Linear: 10 steps, Binary: 5 steps. Best Search is Binary.

Run and edit this code online

This should work for you now.


As for your other problem (seen after editing the question), the problem is that you have the lines:

steps_linear = linear_search(a_list, key)
steps_binary = binary_search(a_list, key)

…in best_search, but you’re calling linear_search and best_search again to compare them:

if linear_search(a_list, key) < binary_search(a_list, key):
     results += "Best Search is Linear."
elif binary_search(a_list, key) < linear_search(a_list, key):
    results += "Best Search is Binary."

Not only is this not needed, it actually messes up the test. If you look at a_list as it progresses through best_search, you’ll notice something:

a_list, before calling any searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a_list, after calling all searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Linear: 1 steps, Binary: 4 steps. Best Search is Linear. 


a_list, before calling any searches: [10, 2, 9, 1, 7, 5, 3, 4, 6, 8]
a_list, after calling all searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Linear: 4 steps, Binary: 4 steps. Best Search is Linear. 


a_list, before calling any searches: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
a_list, after calling all searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Linear: 4 steps, Binary: 5 steps. Best Search is Binary. 


a_list, before calling any searches: [1, 3, 5, 7, 9, 10, 2, 4, 6, 8]
a_list, after calling all searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Linear: 6 steps, Binary: 5 steps. Best Search is Binary. 


a_list, before calling any searches: [5, 1, 8, 2, 4, 10, 7, 6, 3, 9]
a_list, after calling all searches: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Linear: -1 steps, Binary: -1 steps. Result is a Tie. 

After every search, a_list ends up sorted. Why? Because whenever you call binary_search, this is the first thing run:

a_list.sort()

Python’s .sort() modifies the list itself; it does not return a new list. So let’s take a look at best_search again (notice the comments):

def best_search(a_list, key):
    steps_linear = linear_search(a_list, key)
    steps_binary = binary_search(a_list, key) # <-- you call binary_search for the first time, so a_list is sorted
    results = "Linear: " + str(steps_linear) + " steps, "
    results += "Binary: " + str(steps_binary) + " steps. "
    if linear_search(a_list, key) < binary_search(a_list, key): # <-- you call linear_serch and binary_search with the sorted list
        results += "Best Search is Linear."
    elif binary_search(a_list, key) < linear_search(a_list, key):
        results += "Best Search is Binary."
    else:
     results += "Result is a Tie."

    return results

So basically your tests end up to be the same, because you are sorting the list before searching, each time. To fix this, just compare steps_linear to steps_binary without calling the functions again; that’s redundant. Like so:

def best_search(a_list, key):
    steps_linear = linear_search(a_list, key)
    steps_binary = binary_search(a_list, key)
    results = "Linear: " + str(steps_linear) + " steps, "
    results += "Binary: " + str(steps_binary) + " steps. "
    if steps_linear < steps_binary:
        results += "Best Search is Linear."
    elif steps_binary < steps_linear:
        results += "Best Search is Binary."
    else:
        results += "Result is a Tie."

    return results

Output:

Linear: 1 steps, Binary: 4 steps. Best Search is Linear.
Linear: 4 steps, Binary: 4 steps. Result is a Tie.
Linear: 4 steps, Binary: 5 steps. Best Search is Linear.
Linear: 6 steps, Binary: 5 steps. Best Search is Binary.
Linear: -1 steps, Binary: -1 steps. Result is a Tie.

Run and edit this code online

Now the comparison works.

Answered By: marsnebulasoup

to fix below

Linear: -1 steps, Binary: -1 steps. Result is a Tie.

we need to replace "return -1" to "return steps"

Answered By: Babu Mahadevaswamy

Although these answers are correct, it wasn’t running for me until I replaced return -1 with return steps. Replace return -1 with return steps, and it will not reveal the same answer every time.

Answered By: Nick Holmes
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.