Journal of Young Investigators
    Undergraduate, Peer-Reviewed Science Journal
Volume Ten 
    RESEARCH ARTICLE
RECENT ISSUES | ARCHIVES | RESOURCES | JYI NEWS | ABOUT JYI 
Issue 2, February 2004

REVIEW: Engineering & Applied Sciences

The Limits of Computation

Anthony Widjaja
Melbourne University
Discuss this article!

Abstract

We study the limits of computation from a complexity theory standpoint by consid¬ering some incomputable problems and intractable problems. Of practical interest is the NP-complete complexity class, because it contains a wealth of practical problems. However, it is unknown to date whether an efficient algorithm for solving them exists. This question has in fact been classified as one of the seven most important open prob¬lems of this century by the Clay Mathematics Institute. This review discusses some of the most striking consequences of this question in science and life, mentions some evidence of why the answer to this question may be negative, and discusses some approaches researchers have taken for resolving this conjecture. Since the article is written for a broad audience, some basic concepts from computer science and mathematics are reviewed.

 

Introduction

Computers, unarguably, are one of the greatest achievements of humans’ endeavor. Their presence has made a significant impact on the history of mankind — many things once found only in science fiction novels have been made a reality. This raises the question: are there limitations to what computers can do? Some negative answers have been identified in many subfields of computer science. In particular, one can find a plethora of negative results in the field of computational complexity. Therefore, we shall investigate the question from a computational complexity perspective.

Computational complexity — one of the oldest branches of computer science — provides a theory on what is not possible (or feasible) for computers to compute. The theory asserts that some problems, such as the halting problem, are incomputable in a technical sense. Furthermore, even if we restrict ourselves to computable problems, there may still be problems whose solutions take too long to compute, perhaps longer than the age of our universe, even on the best supercomputer available. The most important class of such problems is called NP-complete, which contains numerous practical problems such as the traveling salesman problem, the channel assignment problem, the exam timetabling problem, and the graph coloring problem. The exam timetabling problem, for instance, asks for a conflict-free final exam timetable that minimizes the exam period for a given set of students and a given set of finals. Of course, conflicts arise because a student may take more than one course in a semester. Clearly, this problem is of practical interest, and will be further addressed later in this article.

Even though NP-complete problems are hard in the sense that no efficient algorithm for them is known to date, this does not necessarily mean that one does not exist. In fact, they are open questions that have not been resolved since NP-complete problems were first identified more than 30 years ago (Cook 1971; Levin 1973). The significance of the NP-complete question is indisputable: the Clay Mathematics Institute classifies it as one of the seven most important open problems of the century (together with the famous Poincaré Conjecture, for instance) and promises a reward of $1,000,000 to the first person who provides a logically correct answer (Clay Institute 2003). On the one hand, a positive answer to this question (i.e., that NP-complete problems are computable) will have a positive effect in life, since many practical problems will be able to be solved efficiently. On the other hand, a negative answer to this question imposes a limit of our current model of computation, in which case we may need to look for other ways to solve NP-complete problems, such as searching for approximation algorithms or investigating new models of computation, like quantum and DNA computing.

This article is intended to give a glimpse of the limits of computation to a broad audience. Prerequisite computing knowledge is kept to a minimum; however, a familiarity with basic concepts from mathematics is required. The first section will fix some notations and ter¬minology that will be used throughout the article. The second section will present a concrete incomputable problem. The third section is the heart of the article, where we will see that computability does not imply tractability (i.e., some problems, albeit computable, may require a consider¬able amount of time to compute). In particular, in this section we shall study a number of practical NP-complete problems (along with some ways of attacking them), and discuss the aforementioned famous open question. Finally the article will be concluded in the fourth section. Because some basic notions from graph theory will be used in the third section, readers unfamiliar with the subject will find the information in Appendix A useful.

 

What are Algorithms? What are Problems?

We can think of a deterministic algorithm (or just algorithm) as a precise recipe, procedure, or method that takes an input, carries out the prescribed procedure, and spills an output. Each step is carried out sequentially. Equivalently, an algorithm can also be thought of as a C program that takes an input from the user, carries out the program, and outputs the final result of computation onto the screen.

An algorithm is usually used to compute a problem. So, we have to define what the problem is. Mathematically, we can think of a problem f as a mapping f : I O, where I is a set of possible input strings and O a set of possible output strings. To be useful at all, any algorithm A that computes f must necessarily terminate for any given input i I and outputs f(i). We denote the output of the algorithm on input i I by A(i).

In particular, there is an important class of problems: the decision problems. A problem f : I O is said to be a decision problem if O = {yes”, no”}. We usually denote a “yes” by 1 and a “no” by 0. For notational convenience, we may think of the decision problem f as the set of inputs i such that f(i) = 1. So, whenever suitable, we shall write i f to mean that i is an input of f that satisfies f(i) = 1. Unfortunately, there are much more functions than there are algorithms. In effect, as we shall see later, there are problems which are incomputable; in other words, there is no algorithm that can compute them.

Before going any further, let us see a concrete algorithm that computes a simple decision problem. Consider the problem EVEN of determining whether a given natural number is even. We formulate it as follows:

          Problem:        EVEN

          Input:           a natural number n

          Output:         1 if n is even, 0 otherwise

 

We can describe an algorithm A that computes EVEN as follows:

          A = “On input n where n is a natural number:

          1. If two divides n, then output 1

          2. Otherwise, output 0

First, it is important to note that right after an algorithm gives an output, it must terminate. Now, we see that this algorithm works correctly since a number n is either even or odd and that, by definition, an even number is one that is divisible by two.

Let us now contemplate a slightly harder problem: the problem PRIME of determining whether a number n is prime. We formulate it below:

          Problem: PRIME

          Input: a natural number n

          Output: 1 if n is prime, 0 otherwise

The following simple algorithm computes PRIME:

          B = “On input n where n is a natural number:

          1. If n = 1 or n = 0, output 0

          2. For i = 2 to n 1, do the following:

          3. If i divides n, then output 0

          4. Output 1

In programming, the For statement in the algorithm B basically means: go through the body of the For statement (i.e., the lines indented) with i = 2 for the first loop, i = 3 for the second loop, i = 4 for the third loop, and i = n − 1 for the last loop. Now, to see that B computes PRIME, first observe that, by definition, a prime number n is a number other than 1 and 0 that is only divisible by 1 and the number n itself. Step 1 takes care of the inputs 0 and 1, since they are not prime. Step 2 ensures that if there is a number between 2 and n 1 that divides n, the algorithm will give a 0 since n is not prime. After doing the checking B is required to do, we are assured that n satisfies the definition of prime and thus output 1. Unfortunately, this simple algorithm is inefficient; see Agrawal et al. (2002) for an efficient algorithm.

An Incomputable Problem

Before we discuss NP-complete problems, it will be instructive to find whether there are incomputable problems. After all, the existence of such problems creates an inherent limitation in computers in terms of computability. While our argument in the previous section does not really demonstrate a concrete incomputable problem, we shall encounter one incomputable problem in this section: the famous Halting Problem.

The Halting Problem was first formulated by Alan Turing, one of the pioneers of computer science, at the time he proposed his model of computation: the Turing machine. This problem actually came as a shock, since it was first conjectured that for every function there exists an algorithm — it is just a matter of when we discover it. We define the Halting Problem, calling it HALT, as follows:

          Problem:        HALT

          Input:           Program A and an input I for A

          Output:         1 if A terminates on input I, 0 otherwise

Now, why is this problem incomputable? After all, can’t we just write a program that simulates A on input I and outputs whatever A outputs? This is not the case; for if the input program A does not terminate, then neither will the program that simulates it — failing to compute HALT. I shall only mention that this problem is simply incomputable. However, the readers should be aware that this non-trivial result can be rigorously proved using some diagonalization arguments. Such a proof has been nicely done by, for example, Sipser (1997).

 

Hard Computable Problems

Even if we restrict our attention exclusively to computable problems, there may still be prob­lems that require significant amounts of time to compute. We shall see some in this section. But, first, how do we determine if a problem is hard? How can we tell whether an algorithm performs well? Do we just plug in an input, run the algorithm, and count how many seconds before it terminates? To answer these questions, we shall discuss some techniques to measure the efficiency of algorithms. Two important complexity classes, P and NP, will then be introduced. After that we shall encounter the famous open question of P vs. NP and discuss some recent progress toward answering this question. Then we will see a number of ways of attacking NP-complete problems. Finally, I shall end this section by mentioning some impacts of the question of P vs. NP in practical life.

Asymptotic analysis of algorithms

Computer scientists have developed a general way of measuring the efficiency (i.e., the time complexity or the running time) of an algorithm without actually running the algorithm on some inputs. After all, we want to have an efficiency measure of an algorithm independent of the computers on which we run it. We use a technique called asymptotic analysis, first introduced for analyzing the time complexity of an algorithm by renowned computer scientist Donald Knuth. Basically, this technique tries to mimic how fast the time, which an algorithm uses to solve a problem, grows with the size of its input using some mathematical functions. However, if we were to analyze it in a precise manner, then undoubtedly the functions could be very complicated. This is when asymptotic analysis becomes useful.

In particular, asymptotic analysis provides a means of approximating complicated functions using some very simple functions. For example, suppose that we have algorithms A and B whose running times are, respectively, 1000n2 and 2n where n is the size (length) of the input strings. Although the algorithm B runs faster than A does for small n, the algorithm A clearly outperforms B for large n. To make this notion more precise, we need the following definitions:

Definition 1 (The “big-O notation”): Let f and g be two functions f, g : N N. We say that f(n) = O(g(n)) if there exists positive constants c and n0 such that for all n > n0, f(n) cg(n). We say that g is an upper bound for f. Equivalently, we also say that f is of order g. See Figure 1.

figure 1

Figure 1 . An illustration of the big-O notation. Here, we have f(n) = O(g(n)), and g(n) = Ω(f(n)). We say that the growth of function g(n) is asymptotically faster than the grown of function f(n). That is, after n0, g(n) is always greater than f(n).

 

Definition 2 (The “big-omega notation”): Let f and g be two functions as above. We say that f(n) = Ω(g(n)) if g(n) = O(f(n)). We say that g is a lower bound for f.

In essence, these notations give us a way of saying that some function f is bounded from above (or below) by some function g. One thing to note is that the constant of a function is immaterial in an asymptotic analysis. For instance, 1000n2 and 100000n2 are both of order 2. As another example, we have 10000000 = O(1).

Moreover, these notations allow us to disregards terms of smaller order. Consider the function f(n) = 1000n2 + 4n3 + 8n as an example. Clearly, n3 dominates the growth rate of this function for large n, and, thus, we say that f(n) = O(n3). One last example: the function O(n4) + O(n) + O(2n) is of order 2n . We also write this O(n4) + O(n) + O(2n)= O(2n). All these can be shown with some rigorous arguments starting from the definition. Here are some simple, but useful, functions that we can use for approximating complicated functions:

          f(n) = 1: a constant function. As we saw before, for any positive constant c,

          we have c = O(1).

          f(n) = log(n): a logarithmic function.

          f(n) = n: a linear function.

          f(n) = n2: a quadratic function.

          f(n) = nk: a polynomial function.

          f(n) = 2n : an exponential function.

          f(n) = n! : a factorial function.

How does this measure relate to real computers? Table 1 shows how long it takes for algorithms whose time complexity is f(n) to carry out a task (on a computer on which each operation takes 10-9 seconds). We see that, to begin with, when n = 10, all of these algorithms take about the same amount of time. However, for n = 20, the algorithms whose time complexity is O(n!) become impractical, meaning it takes quite a few years before it finishes. For n > 40, the algorithm whose running time is O(2n) becomes impractical. Well before n = 50, the value of n! exceeds the number of atoms in the universe: 1070 (Gershenfeld 2000).

Table 1

Table 1 .

There are some handy facts to know when analyzing algorithms. First, arithmetic opera­tions (+, , ×, /) can be executed in constant time (i.e., they require O(1) steps). Second, comparison operators also require constant time. Third, an algorithm spends most of its time repeating the same behavior with different parameters (i.e., looping using the For or While statements).

Equipped with this knowledge, let us now try analyzing the time complexity of the algo­rithm A that computes the EVEN algorithm introduced above. Step 1 requires constant time, since divisions can be done in constant time. Outputting 1 or 0 can also be done in constant time. Hence, the algorithm A runs in O(1) + O(1) = O(1) time. Note that the algorithm A has no looping statement.

Now, let us turn our attention to analyzing the algorithm B that computes PRIME. Steps 1 and 4 require constant time. The algorithm repeats step 3 n times. Hence, steps 2 and 3 take O(n) time. In total, the algorithm takes O(n) time. Recall that the complexity of an algorithm is expressed in terms of the size of its input. Let m be the size of the input n. Since n is represented in binary digits in computers, m is the length of the input string n in binary form, which is logarithmic in n. More precisely, m = log2(n) and so n = 2m. So, B indeed runs in time O(2m). Note that a similar argument also shows that B runs in time Ω(2m).

Complexity theorists have shown that an algorithm whose running time is O(nk) for k constant (a polynomial-time algorithm) is strictly more efficient than one whose running time is Ω(2n) (an exponential-time algorithm). But what if the number k in the function O(nk) is large, e.g., 1000? Then the algorithm becomes impractical. However, once there is a polynomial-time algorithm for a given problem, a significant insight into the problem is gained and usually faster algorithms (algorithms with running time O(nk) for small k) follow.

The Complexity Class P

The complexity class P is defined to be the set of decision problems for which there exists a polynomial-time algorithm. Said differently, P is the class of decision problems that computers can compute efficiently.

From our analysis of the algorithm A, for example, we see that EVEN is in P (is efficient). On the other hand, our analysis of the algorithm B does not show that PRIME is in P (neither does it show that PRIME is not in P) since B runs in exponential time. Recently, Agrawal et al. (2002) showed that PRIME is in P — a nontrivial result that has spawned massive interests in theoretical computer science.

Another interesting problem in P is the following problem from graph theory, called PATH (see Appendix A for a discussion of graph theory).

          Problem:        PATH

          Input:           Graph G = (V, E) and two vertices u, v in V

          Output:         1 if there is a path connecting u and v in G, 0 otherwise

Observe that, in practice, PATH may be used to model the problem of determining whether there exists a sequence of flights that can take passenger from an airport a to an airport b. Now, to show that PATH is in P, we need to give a polynomial-time algorithm that computes it. The following simple algorithm C computes PATH:

          C = “On input G = (V, E) and u, v:

          1. Mark the vertex u.

          2. Repeat until the following marks no additional vertices:

                   3. Scan all edges in E. If an edge (a, b) is found where a is marked

                     and b unmarked, then mark b.

          4. Output 1 if v is marked and 0 otherwise.”

It is not hard to see that this algorithm computes PATH. Now, analyzing its complexity, we see that the bottleneck computation is located in steps 2 and 3. The time spent here is bounded above by the number of edges in G which is O(n2) where n = |V| is the size of the input. Hence, this algorithm runs in time O(n2). So, PATH is in P.

The Complexity Class NP

The class NP is the set of computable decision problems for which the answer "yes,” accompa­nied by an evidence for the answer, called a witness, can always be verified in polynomial-time. To get a feel for the concept of witness, observe that there are many problems (in particular, puzzles) in life for which it seems very hard to find an answer; but, once a “witness” is provided, it seems trivial to check the answer.

It is a fact that P NP (Sipser 1997). For example, PATH is in NP. That is, given an input <G, u, v> for PATH, a witness for <G, u, v> P NP PATH is basically a path P in G connecting the two vertices u and v. That P is indeed such a path can be verified in polynomial time. Basically, the verifier needs to check that every edge in P is a valid edge in G and that P actually connects u and v. The running time of this algorithm is bounded from above by the number of edges in G, which is in the worst case O(n2) where n = |V|.

A real-world example of NP problems is the channel assignment problem, which is currently a heavily researched topic (e.g., Roberts 1991; Griggs and Yeh 1992; Whittlesey et al. 1995; Chang and Kuo 1996; Bodlaender et al. 2000). As is the case with many combinatorial problems, this problem is quite simple to state. Given a set of transmitters x1, ..., xn in a region, one wishes to assign a channel (frequency) f(xi) for the transmitter xi for 1 < i < n such that interference is avoided as much as possible. This problem often arises in radio telecommunication.

We shall now study a particular graph-theoretic model of this problem called the L(2,1)-labeling problem. The region in the problem directly translates to a graph G whose vertices are the transmitters x1, ..., xn. We say that two vertices xi, xj, interfere if the distance between xi and xj is at most two. A channel assignment f is a function mapping x1, ..., xn to a set of natural numbers corresponding to channels such that, for any two vertices x, y, we have | f(x) f(y) | > 1 if x and y are adjacent and | f(x) f(y) | > 2 if x and y are a distance two apart. We define the span of f, denoted span(f), to be the difference between the maximum and the minimum channels used in f. Also, f is said to be optimal for a graph G if there is no channel assignment f ’ such that span(f ’) < span(f). The channel assignment problem CAP is defined as follows:

          Problem:        CAP
         
Input:           Graph G = (V, E)
         
Output:         An optimal channel assignment for G

Figure 2 illustrates a sample input and output to CAP. Clearly, CAP is not a decision problem; it is actually called an optimization problem since it attempts to optimize a function. Therefore, to show that CAP is in NP, we need to reformulate this as a decision problem. Given a graph G, let l(G) be the minimum span taken over all possible channel assignment appropriate for G. Now, we may translate CAP to the following decision problem CAP(D) as follows:

          Problem:        CAP(D)
         
Input:           Graph G = (V, E)
         
Output:         1 if l(G) < |V|, 0 otherwise

figure 2

Figure 2. An optimal channel assignment f for a cycle with six vertices. The numbers next to the vertices correspond to channels. Here, span(f) = 4.

 

First, observe that CAP(D) is no harder than CAP; if there is a polynomial-time algorithm for CAP, then there is also a polynomial-time algorithm for CAP(D) (indeed, this argument generalizes to any optimization problem). That is, by showing that CAP(D) is hard, we also show CAP is hard. (We shall discuss what is hard in the next section.) Now, to show that CAP(D) is in NP, we need to show that CAP(D) is computable and that, given a witness for an input G =(V, E), we can check if G CAP(D) in polynomial-time. It can be shown that l(G) < (n2 n) where n = |V| (Chang and Kuo 1996). So, we can basically enumerate every possible function f : V(G) {0, . . ., n2 n} and output 1 if there is a channel assignment f with span(f) < |V|(or output 0 otherwise). Since the number of such functions is finite, CAP(D) is computable. If, instead, G P NP CAP(D), a witness for it is simply a valid channel assignment that satisfies l(G) < |V|. Also, we can verify this in polynomial-time (since the verifier is similar to that of PATH). Hence, CAP(D) is in NP.

Now, I shall illustrate another problem of a slightly different flavor. Recall the exam time­tabling problem referenced in the Introduction. The problem is to assign a time slot to each final exam, while minimizing the exam period. Moreover, since some students may take more than one courses in a given semester but only one exam during any given time slot, some possible conflicts may arise.

We start by discussing a graph-theoretic model of this problem. How do we translate the problem to the language of graph theory? First, let the set V(G) of vertices in the graph G be the set of final exams. For any two final exams u, v in V(G), we draw an edge (u, v) P NP E(G) if and only if there is a student who will be sitting in both exams u and v. Now the problem reduces to the problem of vertex-coloring (or graph-coloring): we wish to assign a color (time slot) to each vertex such that no two adjacent vertices receive the same color. It is easy to see that these two problems are equivalent. Now, let c(G) be the smallest number of colors needed to color G such that no two adjacent vertices receive the same color. Then the exam timetabling problem EXAM can be stated as follows:

          Problem:        EXAM

          Input:           Graph G representing final exams and conflicts

          Output:         An optimal coloring for G and the number c(G)

See Figure 3 for a sample input and output to EXAM. Of course, as before, EXAM is not a decision problem. At this stage, the reader may enjoy devising a decision problem corresponding to EXAM and show that, like CAP(D), it is NP.

figure 2

Figure 3. A sample conflict graph G. Vertices of G are the exams to be scheduled in a given semester (typically, in a real university setting, there can be 500 exams per semester). Edges represent conflict. Colors represent time slots. Here, we have χ(G) = 3.

P vs. NP: The Million-Dollar Question

We have just shown that CAP(D) is in NP, but is CAP(D) in P? Surprisingly, this question is equivalent to the famous open question mentioned in the Introduction. This is because CAP(D) belongs to a special complexity class: NP-complete. To define the notion of NP-completeness, we need the definitions of NP-hardness and polynomial-time reductions. Given two decision problems, P1 and P2, we say that P1 is polynomial-time reducible to P2 if there is a polynomial-time algorithm T such that, for every input iP1 of P1, T modifies iP1 to an input iP2 of P2 in such a way that iP1 P NP P1 if and only if iP2 P NP P2. A problem P1 is said to be NP-hard if every problem P2 in P is polynomial-time reducible to P1. Lastly, a problem P is said to be NP-complete if P is in NP and P is NP-hard (recall that P is a specific problem, whereas P is the complexity class of decisions problems). I shall only mention that there are numerous practical problems, such as the channel assignment problem and the exam timetabling problem, living in the NP-complete class. It is nontrivial to show that a problem is NP-hard. See Sipser (1997) for some proofs of NP-hardness.

It is not hard to check that P = NP if and only if there is an NP-complete problem P which is in