What are algorithms? Why is the study of algorithms worthwhile? What is the role of algorithms relative to other technologies used in computers? In this chapter, we will answer these questions.
Informally, an algorithm is any well-deﬁned computational procedure that takes some value, or set of values, as input and produces some value, or set of values, asoutput. An algorithm is thus a sequence of computational steps that transform the input into the output.
We can also view an algorithm as a tool for solving a well-speciﬁed computational problem. The statement of the problem speciﬁes in general terms the desired input/output relationship. The algorithm describes a speciﬁc computational procedure for achieving that input/output relationship.
For example, we might need to sort a sequence of numbers into nondecreasing order. This problem arises frequently in practice and provides fertile ground for introducing many standard design techniques and analysis tools. Here is how we formally deﬁne the sorting problem:
For example, given the input sequence [31, 41, 59, 26, 41, 58], a sorting algorithm returns as output the sequence [26, 31, 41, 41, 58, 59]. Such an input sequence is called an instance of the sorting problem. In general, an instance of a problem consists of the input (satisfying whatever constraints are imposed in the problem statement) needed to compute a solution to the problem.
Because many programs use it as an intermediate step, sorting is a fundamental operation in computer science. As a result, we have a large number of good sorting algorithms at our disposal. Which algorithm is best for a given application depends on—among other factors—the number of items to be sorted, the extent to which the items are already somewhat sorted, possible restrictions on the item values, the architecture of the computer etc.
An algorithm is said to be correct if, for every input instance, it halts with the correct output. We say that a correct algorithm solves the given computational problem. An incorrect algorithm might not halt at all on some input instances, or it might halt with an incorrect answer. Contrary to what you might expect, incorrect algorithms can sometimes be useful, if we can control their error rate. We shall see an example of an algorithm with a controllable error rate in Chapter 31 when we study algorithms for ﬁnding large prime numbers. Ordinarily, however, we shall be concerned only with correct algorithms.
Algorithms as a technology:
Suppose computers were inﬁnitely fast and computer memory was free. Would you have any reason to study algorithms? The answer is yes, if for no other reason than that you would still like to demonstrate that your solution method terminates and does so with the correct answer.
If computers were inﬁnitely fast, any correct method for solving a problem would do. You would probably want your implementation to be within the bounds of good software engineering practice (for example, your implementation should be well designed and documented), but you would most often use whichever method was the easiest to implement.
Of course, computers may be fast, but they are not inﬁnitely fast. And memory may be inexpensive, but it is not free. Computing time is therefore a bounded resource, and so is space in memory. You should use these resources wisely, and algorithms that are efﬁcient in terms of time or space will help you do so.
Different algorithms devised to solve the same problem often differ dramatically in their efﬁciency. These differences can be much more signiﬁcant than differences due to hardware and software.
As an example, in Chapter 2, we will see two algorithms for sorting. The ﬁrst, known as insertion sort, takes time roughly equal to c1* n^2 to sort n items, where c1 is a constant that does not depend on n. That is, it takes time roughly proportional to n^2 . The second, merge sort, takes time roughly equal to c2*n*lg(n), where lg(n)stands for log(n) and c2 is another constant that also does not depend on n. Insertion sort typically has a smaller constant factor than merge sort, so that c1 < c2 . We shall see that the constant factors can have far less of an impact on the running time than the dependence on the input size n. Let’s write insertion sort’s running time asc1*n* n and merge sort’s running time as c2*n* lg(n). Then we see that where insertion sort has a factor of n in its running time, merge sort has a factor of lg(n), which is much smaller. (For example, when n=1000, lg(n) is approximately 10, and when n equals one million, lg(n) is approximately only 20.) Although insertion sort usually runs faster than merge sort for small input sizes, once the input size n becomes large enough, merge sort’s advantage of lg(n) vs. n will more than compensate for the difference in constant factors. No matter how much smaller c1 is than c2 , there will always be a crossover point beyond which merge sort is faster.
For a concrete example, let us pit a faster computer (computer A) running insertion sort against a slower computer (computer B) running merge sort. They each must sort an array of 10 million numbers. (Although 10 million numbers might seem like a lot, if the numbers are eight-byte integers, then the input occupies about 80 megabytes, which ﬁts in the memory of even an inexpensive laptop computer many times over.) Suppose that computer A executes 10 billion instructions per second (faster than any single sequential computer at the time of this writing) and computer B executes only 10 million instructions per second, so that computer A is 1000 times faster than computer B in raw computing power. To make the difference even more dramatic, suppose that the world’s craftiest programmer codes insertion sort in machine language for computer A, and the resulting code requires 2n^2 instructions to sort n numbers. Suppose further that just an average programmer implements merge sort, using a high-level language with an inefﬁcient compiler, with the resulting code taking 50n*lg(n) instructions. To sort 10 million numbers, computer A takes:
while computer B takes:
By using an algorithm whose running time grows more slowly, even with a poor compiler, computer B runs more than 17 times faster than computer A! The advantage of merge sort is even more pronounced when we sort 100 million numbers: where insertion sort takes more than 23 days, merge sort takes under four hours. In general, as the problem size increases, so does the relative advantage of merge sort.