Binary Search in Python — Is it Faster? | by Martin Andersson Aaberge | Towards Data Science

It’s that time of the week! You decide to dive deeper into a subject and today we are looking at Binary Search.
As always, there are many ways to solve the same problem. When you want to check if an element is in a list, you can do this with a linear search as well as a binary search, but guess which is faster.
If you have watched programming job interviews on Youtube lately, you know that binary search is a favorite.
Why should you spend time learning about binary search? Your C++ programming friends probably told you this already. Python is slow. You want to make sure you don’t make your program slower than it needs to be.
When you learn Python, you learn to do a linear search to check if an element is in a list. That’s fine when you learn to code, but what happens when you have 60.000.000 elements in a list?
If you do a linear search in a list with 11 elements you have to go through all 11 elements. If you use binary search you might end up with as few as 2 iterations depending on what you’re looking for. See the graphics below.
It should be obvious which approach is faster.
You have most likely worked with lists a hundred times already when you started learning Python. Checking if a value is in the list is a normal task and you have seen this before:
my_list = [1,2,3,3,5,11,12]if 11 in my_list:    return Truereturn False
Or this:
my_list = [1,2,3,3,5,11,12]for each in list:    if each==11:        return Truereturn False
Let’s start looking at how you can implement binary search.
Let’s visualize how binary search works.
First, we need to make sure the list is sorted. You can sort your list with either .sort() or sorted() I use .sort() to alter the list in place. If you for some reason need a new list or don’t want to tamper with the original, use sorted()
This is our test material:
bin_list = [1,2,3,5,6,9,11,12,15,20,22]search_value_a = 15
We are going to look for the value 15.
Our starting point. A list with a minimum value and a maximum value:
When we do a binary search, we start by looking for the middle element in the list:
The middle index is 5 with a value of 9. The first thing we want to know is if 9 is the number we are looking for. Remember, we are looking for 15. If it is not, we check if it is lower or higher. In our case, 9 is lower than 15, so we need to set a new min point. We know we don’t need to worry about the lower half of the list anymore. The new min point will be set to the first possible item in the upper part of the list.
With a new midpoint, we check if this is the number we are looking for. In this case, it is.
How do you think the algorithm would behave if we were looking for 2, and our first middle value was 9? You’re right. The max index would move instead.
Ok, I know you came here to code. Let’s get crackin’.
Psuedo code will look like this:
# create function with list and target as parameters.# make sure the list is sorted.# get length of list minus 1 as max and 0 as start.# a loop will:    # get the new middle value    # check if the middle value is higher or lower than the target.     # move the min or max to the middle after the check.     # if middle == target, return True    # if we reach the end of the list, the target is not in the list
We have created a function with two parameters. A list and a target value. The target value is the number we are looking for. The list is the list we iterate through, looking for the number.
def binary_search(input_list , target_value):
If we find the target value, we will return True. If not, we will return False.
The first thing we do is sort the list and define the minimum index of the list and the maximum index of the list.
input_list.sort()min_index = 0max_index = len(input_list) -1
The reason we use len(list)-1 is that Python starts indexing at 0. The length of the test list is 11 , but the last index is [10] .
Now, let’s go to the brain of the function, the loop:
while max_index >= min_index:    mid_index =(max_index+min_index)//2    if input_list[mid_index] == target_value:        return True    elif input_list[mid_index] < target_value:        min_index = mid_index+1    else:        max_index = mid_index-1
As long as the max index is not higher than the min index, we keep going. If the loop stops, that means we have collapsed the list so max is less than min. At this point, there is no point in searching for the value as there is no more list.
mid is set to the average of max and min. Notice how we use floor division/integer division so for example 7//2 would be 3 and not 3.5. This way we always get a clean integer for our index.
If the value of the list item with the mid index is equal to our target value, we made it! Return True and go home.
If the value is less than the target value, we know that we have to push the min index up to that point. The new min is therefore mid+1
If the value is not equal to or less than the target value, it is larger. That means we can kill the top part of our list and push the max index down. max is set to mid-1
If you find it hard to follow along, you can add a print() to the code to get a visual representation of the jumping of indexes.
Add this right after the mid_index =(max_index+min_index)//2 in the while loop:
print (f'min: {min_index} , mid: {mid_index} , max: {max_index}')
main()
Our main procedure contains the test data and a few asserts to check if everything is working as expected.
Notice that I added another way to build the list if you want to see an example where the search fails.
#bin_list = list(range(6,501))
The time complexity of this function is O(n) where n is the length of the list. To check which search is faster, we can time the calculation of a binary search against a linear search.
First, we need to write a linear search function:
def linear_search(input_list, target_value):    for each in input_list:        if each==target_value:            return True    return False
Then, we need to write performance tests, one for binary and one for linear:
If you run the code above (merged with the original), you will see that the linear search is faster. What is this sorcery?
There are several issues giving binary a hard time.
sorting
list length
low target value
All of the factors above, give linear a head start. Let’s keep sorting in there, for now, and alter the list length first:
bin_list = list(range(1,10000))lin_list = list(range(1,10000))
The linear search still has the upper hand. Let’s take sorting out of the function and sort the list before passing it to the function. (This is not fair to the linear search as linear does not rely on sorted lists). All we have to do is comment it out as our lists are sorted anyways.
It’s getting close.
What if we move the target value to 7.500? Now our bias is shining as we really want binary to be faster.
This time the difference is extreme. The last example below will even the odds and make everything fair.
Let’s create a random list at random lengths with random targets. then we will run this 100.000 times for both functions.
As they can not share the list, we will trust the Monte Carlo simulation (while praying the Macbook doesn’t catch fire...)
test_list = list(range(1,random.randint(2,50000)))test_number = random.randint(2,50000)binary_search(test_list,test_number)
Now let’s run it 60 million times! Just kidding, I trust these results. MacBooks are expensive.
Is binary faster than linear, then? Yes, but it depends.
When someone tells you binary search is faster, it is because it generally is.
As always, you have to look at the setting and don’t go for a single solution every time because it “is the best”.
What we saw from our experiments, was that it depends on what you are doing. If you have a short list, or if you are looking for an element in the lower part of a list, you might be better off performing a linear search.
This is also the beauty of programming. You aren’t supposed to use one way of doing something without knowing why you are doing it. If you didn’t know binary search already, now you have yet another tool to do a search. Use it whenever you find a good use for it.
I hope we can agree on one thing though. Binary Search is pretty darn cool!