In computer science, some problems can be solved with algorithms, while others cannot. This topic explores the boundary between solvable and unsolvable problems.
What is a computable problem?
A computable problem is one for which a step-by-step procedure, or algorithm, can be constructed that solves any instance of the problem in a finite amount of time. This means there is a well-defined method that takes the input, processes it, and produces the correct output within a guaranteed number of steps.
The concept of computability is based on a theoretical model of computation called the Turing machine, developed by Alan Turing in the 1930s. If a problem can be solved by a Turing machine, it is considered computable.
Characteristics of computable problems
There exists an algorithm: A sequence of instructions can be written to solve the problem for any valid input.
The algorithm halts: The procedure terminates after a finite number of steps. It does not run indefinitely.
Correct output: The algorithm gives the correct result for each possible input.
Turing machine compatible: The problem can be represented and solved using a Turing machine.
Computable problems make up the majority of problems encountered in day-to-day software applications. These problems are not just theoretically solvable—they are also practically implementable on real-world computers.
Practice Questions
FAQ
Yes, a problem can be partially computable, also known as semi-decidable. This means there exists an algorithm that will return a correct solution if the solution exists, but may run forever if it does not. In other words, the algorithm may halt and produce an answer in some cases, but fail to halt in others. A common example involves Turing machines that recognise certain languages: they accept valid strings by halting, but may never halt for invalid ones. In practical terms, this means we can detect success but not guarantee detection of failure. For instance, a program designed to identify whether another program contains a bug might find the bug and report it—halting with success—but might never halt if the bug isn’t present, because it keeps searching indefinitely. Partially computable problems are significant because they lie between computable and non-computable problems: they offer solutions in some cases, but do not guarantee universal decidability.
Simulating every program to determine whether it halts seems like a logical approach, but it fails due to the sheer number of possible behaviours and the lack of a universal stopping condition. Some programs contain complex or self-referential logic that depends on inputs, conditions, or computations that can only be resolved at runtime—or not at all. While simulation may work for specific examples, it does not scale to a general solution. Moreover, the problem is not with computing power or time, but with logical undecidability. Turing proved that trying to simulate every program in an attempt to decide halting leads to contradictions; specifically, it enables the construction of a program that would behave the opposite of the simulator’s prediction, creating a logical paradox. This isn’t just a technical problem—it’s a fundamental limitation on computation itself. Therefore, no amount of simulation, no matter how clever or powerful, can solve the halting problem for all cases.
Yes, non-computable problems have important implications in real-world software development, particularly in areas like program verification, compiler design, testing, and security analysis. For example, developers often want tools that can automatically verify whether their programs will terminate, avoid infinite loops, or meet certain safety conditions. However, due to the halting problem and related undecidability results, such tools can only ever be partial—they work in many cases, but they can never guarantee correctness across all inputs and program structures. In security, malware detection software faces similar limits: it may successfully flag malicious patterns but cannot guarantee complete detection without false positives or undetected cases. Similarly, static analysers used to detect bugs or optimise code can never fully prove the absence of errors. This forces developers to rely on a mix of automated tools and manual checks, acknowledging that some behaviours may forever evade detection. These theoretical limits translate into very practical challenges.
Non-computable problems influence the design and implementation of programming languages in subtle but important ways. Language designers must decide how much computational power to expose to programmers while also enabling analysis, optimisation, and verification. Languages that allow full access to Turing-complete features (such as arbitrary loops, recursion, and self-modifying code) inherently allow the expression of non-computable behaviours—like writing programs for which we cannot predict termination or detect certain outcomes. This makes advanced static analysis or full proof-of-correctness impossible in general. Some restricted languages or subsets of languages are deliberately not Turing-complete, meaning they exclude constructs that could result in non-termination or undecidability. Examples include domain-specific languages for configuration or hardware description, which are designed to be easier to analyse and reason about. In short, language designers must often trade off expressive power against analysability and predictability, and the limits of computability are central to that decision.
Machine learning and AI cannot solve non-computable problems in the general case because these problems are undecidable in principle, not just practically difficult. However, AI can be used to provide approximate or heuristic solutions. For example, while we cannot create an algorithm that determines with certainty whether a given program halts, we can train a model to recognise patterns or behaviours in code that often lead to halting or non-halting. These predictions may be probabilistically accurate but are never guaranteed to be correct in every case. This is because non-computability is a mathematical limitation, not a technological one—there exists no algorithmic method, no matter how sophisticated, that can decide these problems with absolute certainty. AI can still be useful in prioritising likely cases, flagging potential issues, or guiding human decision-making, but it can’t bypass the core limitations outlined by Turing. At best, it offers insight, not resolution.
