3 Algorithm Analysis-2
3 Algorithm Analysis-2
With the discussion about mathematical concepts, we will proceed with the analysis of
algorithms.
This is very important as we need to identify which algorithm is good or not based on a given
situation.
Create an algorithm that will reverse a given string, and check if it is a palindrome or not,
without depending too much on Python built-in functions.
A palindrome refers to word in which its reverse has the same order of letters.
return is_pal
1.07 µs ± 48.7 ns per loop (mean ± std. dev. of 7 runs, 50,000 loops each)
2.58 µs ± 81.3 ns per loop (mean ± std. dev. of 7 runs, 50,000 loops each)
2.76 µs ± 25.5 ns per loop (mean ± std. dev. of 7 runs, 50,000 loops each)
3.78 µs ± 39.4 ns per loop (mean ± std. dev. of 7 runs, 50,000 loops each)
Primitive Operations
The two methods will be broken down a while later. But first, let us discuss primitive operations.
They usually have a constant execution time in the lowest level of instruction - machine.
However, to reduce the complexity, its 'accurate' execution time is set as 1. The list of primitive
operations are shown below:
y = arr[0]
In this other block of code, it contains four primitive operations. First is to get the value of
0 from the memory, then another retrieval operation for the arr variable, then a
subscripting operation to access the value of the element at that index of the list which is
denoted by arr[0] , and finally storing it into the variable y .
1. Function/method calling
print() # Function
SomeClass.method() #Method
Function calling without a return costs 1 primitive operation to do. Method calling,
however, will cost 2 primitive operations as we the computer needs to get the context of
SomeClass first, before calling method() .
print("Hello World")
When function/method calling with parameters, each parameter will add 2 additional
primitive operations. One is to retrieve the value that will be supplied to the argument, and
storing that value into the argument.
def add(x,y):
return x + y
def getX():
return x
Returning from a function/method call is always constant. However, the number of operations
may vary depending on the return process. For example, the function add(x,y) has four
primitive operations in total that is: (1,2) get the values of x and y from memory, (3) add the two
values, (4) return the value.
Meanwhile, the second function, getX() only contains two primitive operations that is: (1) Get
the value of x from the memory, and (2) return the value.
z = x + y
Every arithmetic operation is always a constant operation, 1 . The operation above has four
operations mainly: (1,2) Retrieving the value of x and y from the memory, (3) adding the
two numbers, (4) storing the result into z
if x > y:
#code here
This is similar to number 3. Here, we have three operations in total: (1,2) getting the value
of x and y, and (3) processing the > operation.
z = my_list[1]
The code above has five operations. Firstly is getting the value of 1 from the memory,(2)
next is retrieving the my_list[] from the memory, (3) followed by the subscripting operation
my_list[1] , (4) next is retrieving the value of my_list[1] from the memory, (5) and
storing that value into z .
z = car.getModel()
Applicable when using classes, whenever we call a class, there is always one primitve
operation involved. The code above has four operations, (1) get the car reference from the
memory, (2) call the method getModel() , (3) retrieving the return value from
getModel() and (4) storing the value into z .
The convenience when dealing with the worst-case is that we are concerned with the largest
degree in a given polynomial. We ignore the coefficients and such, as the term a 0
n
x1
has the
largest factor amongst a polynomial.
3 4 2 4
5n + n + 2n + 100n + 50 => O(n )
Calculating Runtime
In this section, we will be discussing how to calculate algorithm runtimes.
Firstly, let us deal with a simplier function: findMax() , where we will find the maximum value
from a given numeric list. We have to assume that the list is ordered from least to greatest in
order to work up with the worst-case scenario.
i = 0 # 2
while i < len(l): # 5 * (n + 1)
if l[num] > max: # 6 * n
max = l[num] # 5 * n
i = i + 1 # 4 * n
return max # 2
# Total => 20n + 12
# O(n)
Line 2
Line 2 has five total operations, which is (1) getting the value of 0 , (2) getting the reference of
l[] , (3) subscripting operation l[0] , (4) retrieving the value of l[0] , and (5) storing it into
max .
Line 4
Line 4 has 5 primitive operations and one linear-scale operation, and an additional 5 operations
when the operation will become false.
The 6 primitive operations are: (1) retrieving the value of l , (2) supplementing l into len()
as an argument, (3) retrieving the value of len(l) through the function's return, (4) retrieving
the value of 1 , (5,6) supplementing the arguments 1, len(l) into range() , (7) retrieving
the value of the range() , and (8 - linear) for every element on the sequence returned by
range() , sequentially assign the value to num.
Point 8 can be broken down into the following: (1) Get the value of the current index from the
immutable sequence returned by range() , (2) store that value into num
Line 5
Remember that we are dealing with the worst-case scenario here. We will assume that the if
condition will run on every iteration of the loop.
We then multiply 6 by n as this will repeat for every iteration of the for-loop .
Line 6
Line 6 has 5 operations namely: (1) get the value of num, (2) get the reference of l[] , (3)
subscript operation l[num] , (4) get the value of l[num] , and (5) store it into max .
Line 7
The 4 primitive operations are namely: (1,2) Get the value of i and 1 from the memory, (3)
execute the + operation, and (4) store the value into i .
Line 8
There are two primitive operations in line 7 identified as: (1) get the value of max, and (2) return
the value of max.
Total
To get the total runtime, we simply add the runtime for each line which is:
5 + (5n + 5) + 6n + 5n + 4n + 2
= 20n + 12
Big-Oh
When using Big-Oh Notation, O(), we ignore the constants and only get the variable with the
highest exponential value. In this case, we have O(n).