# Complexity as Proof

Complexity is often taught and discussed within computer science as a method of evaluating the performance of algorithms, data structures, and more generally programs. Undergrads and competitive programmers learn about the scaling resource usages of common algorithms, how they compare to one another, and how to determine the bounds of new algorithms, and once they can pass the software engineering interview the work is considered done.

A notion that is less commonly explored is the original intention and use of the concept of complexity: the fact that complexity is a fundamental mathematical property of an object, not a yardstick. This means that by reasoning about the complexity of an algorithm or string, we can use this information to prove further properties about its behaviour. To illustrate this concept, we will explore a simple intuitive explanation of Gregory Chaitin's proof that there are infinite primes, which is really cool and elucidating.

## Complexity of Integers

Consider positive integers. As they increase in size, by how much does the spatial complexity of the integer increase? A simple visual way to reason about this is to consider how many characters a string representing the number needs to have. This is analogous to the amount of information needed in order to completely represent that number. For example, in base 10, all numbers lower than 10 require a single digit: `0, 1, 2, 3, 4, 5, 6, 7, 8, 9`. Numbers lower than 100 require 2: `10, 11, 12...` We can represent this mathematically - take the actual value of the integer log10 plus 1 rounded down, and you have the number of characters you need in order to represent that value in base 10. This applies across all bases, and we can say that in general the representational complexity, or spatial complexity, of any integer n is on the order `O(log(n))`.

Importantly, this is the upper bound - there are some exceptions which we can represent in a much more compressed form. For example, powers of 2 can all be represented as the exponent of 2 that they are, so that `4294967296` can be written as `(2 ^) 32` (2 characters instead of 10). In general, writing a power of 2 in this form requires a complexity of `O(log(log2(n)))`.

However, if we don't want to only represent the powers of 2, our pow2 compression isn't very helpful. There will always be integers that require the full `log(n)` characters in order to represent.

To illustrate this, let's reverse the question. Consider a number written in base 2. Say you have a string of 0s of any length, and I tell you I have flipped a random bit to a 1. How difficult is it to find that bit in the string? This is the same question as asking you to find the value of the string, since you need to know where the 1 bit is to calculate the value. The fact is that in the worst case, the 1 bit is the last bit that you check, and therefore in order to know the value of the binary number, you need to check every digit. This is more clear when we flip a random number of random positions - in order to be sure that you have not missed any 1 bits, you need to check every bit in the string.

Given this, to say that using m bits, we can represent up to `2^m` integers, is another way to say that given an integer n we require at least `log2(n)` bits to always be able to accurately represent any integer. When using higher bases, where each symbol represents more than 1 bit of information, this means we require at least `log(n)` units of information, such as numeric symbols.

## Complexity-based Proof for Infinite Primes

Now we've begun the fundamental shift from thinking about complexity as a way of analysing the relative cost of using a data structure or function to thinking about it as a property we can leverage to prove things about the behaviour of the underlying object.

If we can find two representations that are equivalent (can represent the same set), they must have the same spatial complexity. For example, we have already implicitly shown that the complexity of integers written in base 2 are the same as integers written in base 10. The number of digits required to represent in base 2 are on the order of `O(log2(n))` and in base 10 it is `O(log10(n))`, but they are both `O(log(n))`, because these representations can both describe any positive integer.

Another way we can represent any positive integer is using prime factorisation. This is taken for granted here, but make sure you believe that this is the case before continuing. If any integer can be written as a product of its prime factors, then any fact about complexity that we have shown about must also apply to this representation as well.

We notice that this is a kind of expansion of our earlier power of 2 compression, but instead of limiting ourselves to only 2, we can use any prime number, and therefore can represent many more numbers than we could with only 2 and an exponent.

Now lets assume there are only finitely many primes. How does this affect the computational complexity of the representation? Let's call the "final" prime `X`, denoting the prime factorisation of an integer n as `p(n)`, and follow how the complexity grows as n increases towards and away from `X`. As we increase n towards `X`, the spatial complexity of `p(n)` scales as expected - in order to represent an integer of value n, we require on the order of `O(log(n))` units of information.

However, as we move beyond `X` and "run out" of new prime numbers and n hurtles towards infinity, the complexity of `p(n)` scales much more slowly than our digits representations for n. All of the further numbers can be described as the product of the power of a finite set. To show by exactly how much slower `p(n)` scales, assume that `X`, the largest prime, is 3. Similarly to the pow2 compression earlier, any number we can represent using the products of these numbers can be written as `2^a * 3^b`. In other words, only a and b scale with n, and a and b are exponents, so the total complexity is on the order `O(log(log2(n)) + log(log3(n)))`. What is crucial is that we are now making the additional claim that any integer n can be represented with just these exponents a and b. Expanding this to many prime factors, with k prime numbers, p(n) only requires a complexity of `O(k * log(log(n)))` for positive integers.

If this is true, we have a crazy powerful compression algorithm that can represent any integer with a much less spatially complex representation. However, as we explored above - a representational complexity of `O(log(n))` for all integers is not simply nice to have, it is a fundamental requirement. If we have less information than this, there will always be integers we cannot represent, and therefore since finite primes gives a representational complexity of `O(log(log(n)))`, there must be infinite primes.