Linear Time vs. Logarithmic Time — Big O Notation | by Jhantelle Belleza | Towards Data Science

Hello there! I’m finally back after more than 2 months of not writing/touching code. I got caught up with the all the wedding preparation and planning. It’s hard pulling off an overseas wedding without a wedding planner. But it was all worth it since our wedding was one for the books! Wedding fever’s over and now it’s time to recollect my thoughts and get back to coding (still in Swift).
It was hard to decide what to review first before I start job hunting. I figured I should start with the classics — algorithms and data structures, the core of programming. In any system, as data grows, performance shouldn’t be an issue if the correct algorithm has been used.
Today, I’m writing a quick blog about 2 types of Big O Notations, Linear and Logarithmic algorithms.
A little background on Big O Notation first.
Asymptotic analysis is based on mathematical computations that basically measures the efficiency of an algorithm as input dataset grows (thanks Wikipedia!). There are other types of asymptotic analysis depending on where it is applied, but in Computer Science, it’s commonly formatted as Big O Notation.
To easily understand Big O Notation, we’ll compare these two algorithms: Linear — O(n) and Logarithmic — O(log n).
As an example, we’ll try to look for a number in a sorted array.
let numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In Linear or “brute force” technique, performance is dependent on the input size. For our example, we’ll read through each item in the array and search for the number we need. Easy peasy!
//Logarithmic Time - O(n)func linear(key: Int) {for (index, number) in numberList.enumerated() {     if number == key {         print("Value found in index \(index)")        break  } }}
Of course that’s going to work for our small dataset, but it’s not very efficient once you have thousands, even millions of data.
Using binary search — which is a form of logarithmic algorithm, finds the median in the array and compares it to the target value. The algorithm will traverse either upwards or downwards depending on the target value being higher than, lower than or equal to the median.
func binarySearch(key: Int, iminIndex: Int, imaxIndex: Int) {let midIndex = round(Double(iminIndex + imaxIndex)/2)let midNumber = numberList[Int(midIndex)]var result = ""//using recursion, we can go up or down the array and reduce the rangeif midNumber > key { //target is less than the median so traverse downwards   binarySearch(key: key, iminIndex: iminIndex, imaxIndex:   Int(midIndex) - 1) } else if midNumber < key { //target is greater than the median so traverse upwards   binarySearch(key: key, iminIndex: Int(midIndex) + 1, imaxIndex: imaxIndex) } else if midNumber == key { //Found it!  print("Found it at index \(midIndex)")} else {     print("Value \(key) not found")}}
The bulk of this algorithm is at the beginning, but it slowly flattens out as we discard the irrelevant range from the array and continue halving until we find the target value.
Through the graph below, we can easily compare how most algorithms have similar performance with smaller datasets but continuously differs as data grows.
As you can see there are other types of Big O Notation which I’ll discuss in the next days. Ciao for now!
References:
Swift Algorithms and Data Structures by Wayne Bishop
Beginner’s Guide to Big O Notation