Section Twelve: Algorithms Flashcards
Chapter 59 – Analysis and design of algorithms
Comparing algorithms
Algorithms may be compared on how much time they need to solve a particular problem. This is referred to as the time complexity of the algorithm. The goal is to design algorithms which will run quickly while taking up the minimal amount of resources such as memory.
In order to compare the efficiency of different algorithms in terms of execution time, we need to quantify the number of basic operations or steps that the algorithm will need, in terms of the number of items to be processed.
Chapter 59 – Analysis and design of algorithms
A linear function
A linear function is expressed in general terms as f(x) = ax + c
Chapter 59 – Analysis and design of algorithms
A polynomial function
A polynomial expression is expressed as f(x) = axm + bx + c
Chapter 59 – Analysis and design of algorithms
An exponential function
An exponential function takes the form f(x) = abx.
This function grows very large, very quickly!
Chapter 59 – Analysis and design of algorithms
A logarithmic function
A logarithmic function takes the form f(x) = a logn x
Chapter 59 – Analysis and design of algorithms
Permutations
The permutation of a set of objects is the number of ways of arranging the objects. For example, if you have 3 objects A, B and C you can choose any of A, B or C to be the first object. You then have two choices for the second object, making 3 x 2 = 6 different ways of arranging the first two objects, and then just one way of placing the third object. The six permutations are ABC, ACB, BAC, BCA, CAB, CBA.
The formula for calculating the number of permutations of four objects is 4 x 3 x 2 x 1, written 4! and spoken as “four factorial”. (Note that 10! = 3.6 million so don’t try getting 10 students to line up in all possible ways!)
Chapter 59 – Analysis and design of algorithms
Big-O notation
Now that we have got all the maths out of the way and hopefully understood, we can study the so-called Big-O notation which is used to express the time complexity, or performance, of an algorithm. (‘O’ stands for ‘Order’.)
Chapter 59 – Analysis and design of algorithms
O(1) (Constant time)
O(1) describes an algorithm that takes constant time (the same amount of time) to execute regardless of the size of the input data set.
Suppose array a has n items. The statement
length = len(a)
will take the same amount of time to execute however many items are held in the array.
Chapter 59 – Analysis and design of algorithms
O(n) (linear time)
O(n) describes an algorithm whose performance will grow in linear time, in direct proportion to the size of the data set. For example, a linear search of an array of 1000 unsorted items will take 1000 times longer than searching an array of 1 item.
Chapter 59 – Analysis and design of algorithms
O(n2) (Polynomial time)
O(n2) describes an algorithm whose performance is directly proportional to the square of the size of the data set. A program with two nested loops each performed n times will typically have an order of time complexity O(n2). The running time of the algorithm grows in polynomial time.
Chapter 59 – Analysis and design of algorithms
O(2n) (Exponential time)
O(2n) describes an algorithm where the time taken to execute will double with every additional item added to the data set. The execution time grows in exponential time and quickly becomes very large.
Chapter 59 – Analysis and design of algorithms
O(log n) (Logarithmic time)
The time taken to execute an algorithm of order O(log n) (logarithmic time) will grow very slowly as the size of the data set increases. A binary search is a good example of an algorithm of time complexity O(log2n). Doubling the size of the data set has very little effect on the time the algorithm takes to complete.
Chapter 59 – Analysis and design of algorithms
O(n!) (Exponential time)
The time taken to execute an algorithm of order O(n!) will grow very quickly, faster than O(2n).
Suppose that the problem is to find all the permutations of n letters. If n=2, there are 2 permutations to find. If n=6, there are 720 permutations – far more than 2n+1, which is only 64.
Chapter 59 – Analysis and design of algorithms
Calculating the time complexity of an algorithm
Here are two different algorithms for finding the smallest element in an array called arrayX of size n. Assume the index starts at 1.
The first algorithm puts the first value in the array equal to a variable called minimum. It then compares each subsequent item in the array to the first item, and if it is smaller, replaces minimum with the new lowest value.
minimum = arrayX[0]
for k = 1 to n - 1
if arrayX[k] < minimum then
minimum = arrayX[k]
endif
next k
To calculate the time complexity of the algorithm in Big-O notation, we need to count the number of basic operations it performs. There is one initial statement, and n if statements, so the time complexity is 1 + n. However, as we have already discussed, the 1 is insignificant compared to n and this algorithm therefore executes in linear time and has time complexity O(n).
Chapter 60 – Searching algorithms
Linear search
Sometimes it is necessary to search for items in a file, or in an array in memory. If the items are not in any particular sequence, the data items have to be searched one by one until the required one is found or the end of the list is reached. This is called a linear search.
Chapter 60 – Searching algorithms
Time complexity of linear search
We can determine the algorithm’s efficiency in terms of execution time, expressed in Big-O notation. To do this, you need to compute the number of operations that the algorithm will require for n items. The loop is performed n times for a list of length n, and there are two steps in the loop (an IF statement and an assignment statement), giving a total of 3 + 2n steps (including 3 steps at the start). The constant term and the coefficient of n become insignificant as n increases in size, and the time complexity of the
algorithm basically depends on how often the loop has to be performed in the worst-case scenario.
Chapter 60 – Searching algorithms
Binary search
The binary search is a much more efficient method of searching a list for an item than a linear search, but crucially, the items in the list must be sorted. If they are not sorted, a linear search is the only option. The algorithm works by repeatedly dividing in half the portion of the data list that could contain the required data item. This is continued until there is only one item in the list.
Chapter 60 – Searching algorithms
Binary search algorithm
The ordered array is divided into three parts; a middle item, the first part of the array starting at aList[0] up to the middle item and the second part starting after the middle item and ending with the final item in the list. The middle item is examined to see if it is equal to the sought item.
If it is not, then if the middle item is greater than the sought item, the second half of the array is of no further interest. The number of items being searched is therefore halved and the process repeated until the last item is examined, with either the first or second half of the array of items being eliminated at each pass.
Chapter 60 – Searching algorithms
Time complexity of binary search
The binary search halves the search area with each execution of the loop – an excellent example of a
divide and conquer strategy. If we start with n items, there will be approximately n/2 items left after
the first comparison, n/4 after 2 comparisons, n/8 after 3 comparisons, and n/2i after i comparisons. The number of comparisons needed to end up with a list of just one item is i where n/2i = 1. One further comparison would be needed to check if this item is the one being searched for or not.
Solving this equation for i, n = 2i
Taking the logarithm of each side, log2 n = i log2 2 giving i = log2 n (since log2 2 = 1)
Therefore, the binary search is O(log n).
Chapter 60 – Searching algorithms
A recursive algorithm
The basic concept of the binary search is in fact recursive, and a recursive algorithm is given below. The procedure calls itself, eventually “unwinding” when the procedure ends. When recursion is used there must always be a condition that if true, causes the program to terminate the recursive procedure, or the recursion will continue forever.
Once again, first, last and midpoint are integer variables used to index elements of the array, with first starting at 0 and last starting at the upper limit of the array index.
Chapter 60 – Searching algorithms
Binary tree search
The recursive algorithm for searching a binary tree is similar to the binary search algorithm above, except that instead of looking at the midpoint of a list, or a subset of the list, on each pass, half of the tree or subtree is eliminated each time its root is examined.
Chapter 60 – Searching algorithms
Time complexity of binary tree search
Like the binary search, the number of items to be searched is halved with each pass through the algorithm. The time complexity is the same as the binary search, i.e. O(log n).
Chapter 61 – Bubble sort and insertion sort
Sorting algorithms
Sorting is a very common task in data processing, and frequently the number of items may be huge, so using a good algorithm can considerably reduce the time spent on the task. There are many different sorting algorithms and we will start by looking at a simple but inefficient example.
Chapter 61 – Bubble sort and insertion sort
Bubble sort
The Bubble sort is one of the most basic sorting algorithms and the simplest to understand. The basic idea is to bubble up the largest (or smallest) item to the end of the list, then the second largest, then the third largest and so on until no more swaps are needed.
Suppose you have an array of n items:
- Go through the array, comparing each item with the one next to it. If it is greater, swap them.
- The last element of the array will be in the correct place after the first pass
- Repeat n-2 times, reducing by one on each pass the number of elements to be examined