In this last unit of the course, we’ll study problems that are difficult to solve efficiently, or solve at all. Many problems that you might want to solve fall into this category, some of which we’ve seen already. Here are some examples:
We saw one approach to solving some of these problems in Lecture 5.3: try a greedy algorithm that can approximate the solution well. To summarize what will happen in these last two weeks,
To talk about problems that easy and hard, we need to define what these words mean. First, it will be useful to limit our view to decision problems
Definition. A decision problem is a problem
whose answer to every input is either yes
or no
.
Many other kinds of problems can be converted to and from a
decision version without much effort. For example, if you had an
optimization question, like What is the distance between s and t in this graph?
, the decision
version would take an additional input k and ask, Is the distance between
s and t at least k?
If you could solve the optimization version, you can definitely solve the decision version. If you can solve the decision version and wanted to solve the optimization version, you can binary search through different values of k until you find exactly the distance between s and t.
When we talk about a decision problem being easy
or
fast
to solve, we usually mean that it belongs to the class
P.
Definition. P (polynomial time
) is the
class of decision problems that can be solved with an algorithm
that runs in time O(n^c) for some
constant c, where n is the size of the input.
This includes running times like O(n \log n), since if a function is O(n \log n), then it is also O(n^2). The vast majority of decision problems that you have seen in this class (and decision versions of optimization problems) belong to P.
There are two important things to note about P:
Definition. NP (nondeterministic polynomial
time
) is the set of decision problems for which you can
check whether a sample solutions works or not in O(n^c) time, for some c.
A common misconception is that NP stands for not polynomial
time
. That’s not true! NP is still a statement about the
problem being somewhat easy. In particular, it’s easy to check.
We’ll talk more about what nondeterministic
means in this
setting on Monday. For now, just think NP = easy to
check
.
Examples of NP problems include:
Problems that are easy to check can always be solved as follows: Enumerate all possible solutions. There are typically something some exponential number of these, for example, at most 3^n possible colorings of the n vertices for graph 3-coloring. Use the checking algorithm to brute-force check every possible solution. This certainly works, but takes exponential time.
There are still more complexity classes beyond P and NP. For example, EXP is the set of decision problems that you can solve in O(2^{n^c}) time, for some c. As we said before, NP is a subset of EXP. Problems like the Halting problem mentioned earlier actually cannot be solved at all, placing them outside of even EXP. But P and NP are the two most important complexity classes.
Most mathematicians and computer scientists believe that NP is
a strictly larger set than P. Intuitively, you know from
experience that checking answers is usually easier than solving
problems. But we actually don’t know for sure. At the same time,
we have built financial systems and a myraid of other software
based on the assumption that some problems are fast to check but
slow to solve. Our world depends on this hypothesis. This is one
of the most foundational open questions to resolve in computer
science, called P vs. NP
.