Cayley’s formula

In the last post I’ve introduced the term of trees and proved a theorem that allows us to fully understand trees. They are connected, they have n - 1 edges (where n is the number of vertices) and they have no cycles.

I’ve also presented the term of a spanning sub-graph – which is a sub-graph H of a graph G such that V(H)=V(G).

Today I would like to discuss about spanning trees – which are exactly what you thinks they are: A spanning subgraph which is also a tree.

The spanning tree here marked in grey

As it turns out, spanning trees are very important, therefore, people started wondering how many spanning trees are there in a graph.

Cayley’s formula gives us a (partial) solution, it tells us how many spanning trees are there in the complete graph K_n. For example, consider all the spanning trees in K_3:

There are only 3 here, but that makes sense cause we don’t have too many options. What about K_4?

Well… that’s escalated quickly! There are 16 spanning trees in contrast to only 3 in K_3 – I don’t even want to start drawing the spanning trees of K_5… But if you’re wondering, there are 125 of them.

The formula

I am going to denote the number of spanning trees of K_n as t_n. I want you to notice some stuff before I am presenting the formula:

You may notice that some spanning trees are isomorphic to each other, for example, all of the spanning trees of K_3 are isomorphic. However, I still consider them as different trees. Why? Let’s name the vertices of K_3:

I think it is clearer now, for example, the left spanning tree in the example is the graph:

T_1=(\{1,2,3\},\{\{1,2\},\{1,3\}\})

On the other hand, the graph on the middle is:

T_2=(\{1,2,3\},\{\{1,2\},\{2,3\}\})

Clearly, they are not the same, but The only thing that makes them really different is the lable of the vertices. Thus, we are counting labeled trees, and that’s an important thing to understand – If we considered isomorphic trees as the same, we would have get that the number of spanning trees in K_3 is one – and that is not what I am aiming for.

I also want you to understand that we can treat any graph with n vertices as a subgraph of K_n. Therefore, our question is equivalent to the question: “How many trees with n vertices are there?”

OK, we are now ready to present Cayley’s formula: The number of spanning trees of K_n is exactly n^{n-2}.

This formula explains why the number of spanning trees ‘grew up’ so fast when we moved from K_3 to K_4. Notice how simple the formula is – it is very short and even a high-schooler can understand it.

Goals for this post

After we’ve seen Cayley’s formula, we also need to prove it and understand why it works. The proof I will bring today is a proof from 1981 by Joyal. The beautiful thing about this proof, is that it gives us an algorithm to randomally generate trees! Therefore the rest of the post will be splitted into 3 parts:

  1. Some preparations for the proof.
  2. The proof.
  3. An actual algorithm that generates trees.

In order to understand the algorithm, you need to understand the proof as well, however, if you understand the proof and you are not a fan of algorithms, you can skip this part without a problem, since I will not use it in the upcoming posts. However, I do suggest you to check it out, in my opinion, it is really cool to see how we can make the computer do the hard work for us in such a simple way.

preparations for the proof

I only want to prove two small lemmas, suppuse that T is a tree, then:

  1. There is a unique path between any two vertices.
  2. If you add one edge to T, you will create only one cycle.

The proofs are really easy:

  1. Aiming for contradiction, suppose that there are two paths between u and v. Therefore, we can find a cycle in the graph (convince yourself!) and that’s a contradiction to T being a tree (since a tree has no cycles).
  2. Suppose that adding one edge \{u,v\} to T creates two cycles: \{u,v,u_1,\dots,u_k,u\} and \{u,v,u^\prime_1,\dots,u^\prime_k,u\}. We now remove the added edge and we get that \{v,u_1,\dots,u_k,u\} and \{v,u^\prime_1,\dots,u^\prime_k,u\} are two different paths between u and v, and that’s a contradiction to the first statement!

Ok, I think we are ready to prove Cayley’s formula.

The proof

The proof is going to be done step by step, since it is a little bit complicated and since brinining the proof organized and completemakes it much easier to understand.

Part 1 – Describing our plan

My goal is to define a bijection between the set of functions from [n]=\{1,2,\dots,n\} to itself:

[n]^{[n]}=\{f:[n]\to[n]\}

To the sets of triples:

\{(T,L,R)\}

Where T is a tree, and L,R are some vertices in the tree (they can be the same).

If we will manage to find a bijection, this would imply that the sets have the same number of elements:

|\{(T,L,R)\}|=|[n]^{[n]}|

Recall that the number of function from [n] to itself is exactly n^n (we have n options for the output of each element from the set, and there are n elements in the set). Thus:

|\{(T,L,R)\}|=n^n

On the other hand:

|\{(T,L,R)\}|=t_n\cdot n\cdot n=t_n \cdot n^2

Recall that t_n is the number of trees, and L,R are just arbitrary vertices, and there are n vertices, therefore, there are n different options for the value of L and R.

We can now compare both sizes of {(T,L,R)} to get:

t_n \cdot n^2=n^n\Rightarrow t_n=n^{n-2}

And that’s exactly what we wanted!

So In the proof I will describe such a bijection, and I’ll start by creating a map from [n]^{[n]} to (T,L,R).

Part 2 – From function to directed graph

Suppose that f:[n]\to[n] is some arbitrary function. We can represent f like that:

\left(\begin{array}{cccc}
1 & 2 & \cdots & n\\
f(1) & f(2) & \cdots & f(n)
\end{array}\right)

There’s nothing special here, this is just a comfortable way to represent to function. I am going to define now the functional graph of fG_f which is a graph that describes how f works – It is a directed graph that an edge starts at some i\in [n] and ends at f(i). Formally:

G_f=([n],\{(i,f(i)):1\leq i\leq n\})

For example, consider the function:

f=\left(\begin{array}{cccccccc}
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8\\
3 & 4 & 2 & 3 & 5 & 7 & 6 & 1
\end{array}\right)

It’s functional graph is:

It is a visual object that tells us how the function f operates. However, we can learn much more from it.

Part 3 – What can we learn from the functional graph?

The first thing we can see, is that the graph doesn’t have to be connected, so we will explore each components separately. Now, observe that from every vertex, there is exactly one edge coming out of it – which is the edge (i,f(i)). Therefore, the number of edges in each component equals to the number of vertices in the component (you can think of a vertex and an edge coming out of it as a pair – every directed edge has a starting vertex, and every vertex has only one directed edge coming out of it).

Suppose that in some components there are m vertices, thus, there are m edges as well. Since the component is a connected graph by definition, we conclude that it is actually a tree with an extra edge! By the lemma I’ve proved before, we know that the component has exactly one cycle.

I want to mention one more thing though, notice that if you pick some arbitrary vertex i, there is only only way to ‘get out’ it – since there is exactly one edge coming out of it (which is (i,f(i))).Therefore, if you start a walk in an arbitrary vertex, sooner or later you will find yoursef inside a circle! If you want a formal proof for it (as I wanted) here you go:

Quick explanation

Start an arbitrary walk on some component with k vertices, every time you reach a new vertex you label it as ‘visited’ (The first vertex will be marked as ‘visited’ as well). Statring the walk in a vertex i, from there going to the vertex f(i) and so on. We repeat it k+1 times.

If during the process we’ve reached a ‘visited’ vertex, this means we’ve completed a cycle, therefore, we started from an edge pointing towards the cycle.

Notice that this will always be the case. After the first k steps on the walk, in the ‘worst’ case, we visited each vertex exactly once, then in the next step, we must visit a ‘visited’ vertex.

Let me illustrate it:

In our example. Let’s start our walk from 8, mark 8 as ‘visited’ and then proceed to f(8) which is 1. Mark 1 as visited and proceed to 3. There are 5 vertices in the components of 8. After 5 steps, the walk we get it:

8\to1\to3\to2\to4

And all now all the vertices in the component are marked as ‘visited’. The next vertex in the walk is going to be f(4)=4 which is already visited, thus:

3\to2\to4\to3

Is a cycle in the component, and we found out that the edge from 8 actually brings us closer to the cycle.

Part 4 – Creating the tree

We are know finally ready to construct the tree. First define M to be the set of vertices which are in a cycle. In our example, M=\{2,3,4,5,6,7\}. Now there are 2 observations:

  1. If v\in M, then f(v)\in M. By the construction of M, v is a part of a cycle, since only one edge comes out of v, then this edge must end in vertex u which is a vertex of the same cycle as v. Thus, f(v)=u\in M. This allows us to treat f|_M as a function from M to itself: f|_M:M\to M.
  2. f|_M is onto. Suppose that u\in M, then it is part of a cycle, thus, there must be some v in the cycle such that there is an edge from v to u. v is a part of a cycle, thus v\in M, and we know that f(v)=u.

Now, since f|_M:M\to M is onto and has the same domain and rangle (which have the same number of elements) we conclude that f|_M is a bijection!

This fact allows us to write f|_M as a permutation of the element of M=\{v_1,v_2,\dots,v_k\}:

f|_M=\left(\begin{array}{cccc}
v_{1} & v_{2} & \cdots & v_{k}\\
f(v_{1}) & f(v_{2}) & \cdots & f(v_{k})
\end{array}\right)

Where v_1 < v_2 < \dots < v_k.

We now define: L:= f(v_1), R:= f(v_k). and create a path:

P=(f(v_1),f(v_2),\dots,f(v_k))

Since f|_M is a bijection, it is indeed a path made of the vertices v_1,\dots,v_k (convince yourself!).

We now add all the vertices outside of M, and the edges coming out from them to out path to get a tree.

In our example:

f|_M=\left(\begin{array}{cccccc}
2 & 3 & 4 & 5 & 6 & 7\\
4 & 2 & 3 & 5 & 7 & 6
\end{array}\right)

L= 4, R = 6, and the path P will be:

The rest of the vertices are 1 and 8, and the edges are \{1,3\} and \{8,1\}. Adding it to the path to get the tree T:

And we now have the triple (T,4,6).

Part 5 – proving that we got a tree

Our construction is good and we like it very much, however, we didn’t even proved that it is indeed a tree! So let’s prove it:

It has n-1 edges: the set M contains k vertices and created a path, thus it ‘donates’ to the tree k-1 edges. Moreover, there are n-k vertices outside of M, where each one ‘donates’ one edge (which is (i,f(i))). Therefore, there are n-k edges coming from vertices outside of M. Thus we have a total of (k-1) + (n-k) = n-1 edges, as we wanted.

It is connected: Pick two vertices v,u. If they are from the same component in the functional graph, then there is a path between them.

Otherwise, there is a path P_1 from v to v^\prime where v^\prime is a vertex from the same component of v, and it is part of a cycle (This one follows from this fact)

Similarly, there is a path P_2 from u to u^\prime where u^\prime is a vertex from the same component of u, and it is part of a cycle.

There is also a path P_3 between v^\prime and u^\prime, since both of them are part of cycles, therefore the path P_1\cup P_3 \cup P_2 is a a path between u and v.

Part 6 – proving the map is a bijection

Perfect, we now found a way to create a tree out of a function f:[n]\to [n], (this way can easily be translated to an algorithm, which I’ll discuss about later). However, we still need to prove that the function is a bijection. But that’s easy, we can do everything we did here from end to start to get from the triple, the initial function:

Given a triple (T,L,R) find a path between L to R and denote it by P=(L,v_2,...,v_{m-1},R).

The path is made of integeres so we can sort it. Now consider the permutation:

\left(\begin{array}{ccccc}
u_{1} & u_{2} & \cdots & u_{m-1} & u_{m}\\
L & v_{2} & \cdots & v_{m-1} & R
\end{array}\right)

Where u_1,\dots,u_m are just the elements of the path sorted.

We can now define a function f\prime as:

f^\prime(u_1)=L,f^\prime(u_2)=v_2,\dots,f^\prime(u_m)=R

For example, in this tree:

We have L=4, and R =  6. The path is P=(4,2,3,5,7,6). Sotring the element to get the premutation:

f^\prime=\left(\begin{array}{cccccc}
2 & 3 & 4 & 5 & 6 & 7\\
4 & 2 & 3 & 5 & 7 & 6
\end{array}\right)\begin{array}{c}
\text{:sorted}\\
:P
\end{array}

Then we exapnd f^{\prime} to all the vertices that are incident to vertices in P, and define for each such vertex i which is incident to j\in P, the directed edge (i,j) or j=f^\prime(i). If all vertices are now in the domain of f^\prime we are done and we’ve created a function f:[n]\to [n]. If not, we add the vertices that are adjacent to one of those we added in the previous step, and since the number of vertieces it finite, the process must come to an end.

In our example, first we add 1 to the domain and define f^\prime (1)=3. Then we add 8 to the domain and define f^\prime(8) = 1. This yields the function:

f=\left(\begin{array}{cccccccc}
1 & 2 & 3 & 4 & 5 & 6 & 7 & 8\\
3 & 4 & 2 & 3 & 5 & 7 & 6 & 1
\end{array}\right)

And that’s exactly the function we started with!

In cocnlustion, we just proved that this map is invertiable, thus, it is an injection and the prove is done!

The algorithm

I am now going to show you how to implement this prove in code, I am going to construct a function that get’s an array that will represent a function, and it will print what are L,R and the edges of the tree.

I am going to write to code in Java, however, it is really not that complicated, and will be very similar in other languages.

I will implement the method:

public void treeFromFunction(int[] f)

The input is an array of integers that will represent a function f:[n]\to [n]. How exactly? that’s simple – the value of f(n) is just f[n]. However, recall that arrays start with index 0, thus out function will be slightly different, it will be a function from \{0,\dots,n-1\} to itself.

The implementation

I am going to do it step by step:

  1. Denote by n the length of the array.
  2. Find the vertices that are part of cycles in the functional graph (which can be easily represented using the array)
  3. Creating the tree – Setting up the path, and adding the rest.
  4. Defining the values of L,R
  5. printing L,R and the edges of the tree.

This is how the method looks like:

    public void treeFromFunction(int[] f) {
        //part 1
        int n = f.length;
        
        //part 2
        int[] verticesOfCycles = getVerticesOfCycles(n, f);
        Arrays.sort(verticesOfCycles);
        int lastIdx = verticesOfCycles.length - 1;

        //part 3
        int[][] tree = createTree(f, verticesOfCycles, n);

        //part 4
        int L = f[verticesOfCycles[0]];
        int R = f[verticesOfCycles[lastIdx]];

        //part 5
        System.out.println("L = " + L + ", R = " + R);
        printTree(tree, n);

    }

Part 1 is pretty clear, nothing special there. Part 2 is where things get interesting, I am using a method that finds all the vertices that are part of a cycle.

Let’s see how this method works:

Finding vertices of cycles

The method is based on the idea that I have desribed here. I am going to iterate over each vertex i and do the following

  • Create an array called ‘visited’ that will help me to indicate if a vertex is visited or not
  • Start a walk from the specific vertex i, and mark visited vertices
  • If I reach a visited vertex, it means that a cycle was found. Now there are two options:
    • The visited vertex is i – Then i is part of a cycle, so I’ll add it to the list
    • The visited vertex is not i – Then even though we found a cycle, i is not a part of it, so I’ll proceed to the next vertex.

Notice that I will always complete a cycle, no matter what vertex I am starting from. It follows from this fact.

In this process I end up with a list of vertices that are part of a cycle (which is guaranteed not to be empty by the proof) and the only thing left to do is just translate the list into an array and return the array.

This is the code for this method:

    int[] getVerticesOfCycles(int n, int[] f) {
        //iterating over all the vertices.
        List<Integer> listOfVerticesOfCycles = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            boolean[] visited = new boolean[n];
            int current = i;
            while (true) {
                // case where we got back to where we started
                if (current == i && visited[current]) {
                    listOfVerticesOfCycles.add(i);
                    break;
                }
                visited[current] = true;
                int next = f[current];
                if (visited[next] && next != i) break;
                else current = next;
            }
        }
        int[] verticesOfCycles = new int [listOfVerticesOfCycles.size()];
        for(int i = 0; i < verticesOfCycles.length; i ++) {
            verticesOfCycles[i] = listOfVerticesOfCycles.get(i).intValue();
        }
        return verticesOfCycles;
    }

(For some reason it when I type “&” the output is “&amp;” so be aware of that…)

The only part left to explain is how I represent the tree. As you can see, the tree is a 2D array in my code. Why? since I am going to represent it using the adjacency matrix. I have discussed about this matrix in this post, so if you are not familiar with it, you are more than welcome to check out this post.

Setting the tree

There are two types of vertices:

  • Those who part of cycles
  • The rest

We will go over all the vertices and do the following, assuming that the set of vertices of cycles is M=\{v_1,...,v_k):

  • If the vertex is an element of m, so it equals to v_m for some 1\leq m < k. Then we will add the edge \{f(v_m),f(v_{m+1})\}. However, if m = k than v_{m+1} doesn’t exists, so we will do nothing.
  • Otherwise v\notin M, then we will just add the edge \{v,f(v)\}.

Here is the code for this method:

    int[][] createTree(int[] f, int[] verticesOfCycles, int n) {

        int[][] tree = new int[n][n];

        for (int i = 0; i < n; i++) {
            if (isInArray(verticesOfCycles, i)) {
                int idx = indexOfInArray(verticesOfCycles, i);
                if (idx != verticesOfCycles.length - 1) {
                    int nextIdx = verticesOfCycles[idx + 1];
                    tree[f[i]][f[nextIdx]] = 1;
                }
            } else{
                tree[i][f[i]] = 1;
            }
        }

        return tree;
    }
    //helpers
    boolean isInArray(int[] arr, int v) {
        for(int i = 0; i < arr.length; i ++) {
            if (arr[i] == v) return true;
        }
        return false;
    }
    int indexOfInArray(int[] arr, int v) {
        for(int i = 0; i < arr.length; i ++) {
            if (arr[i] == v) return i;
        }
        return -1;
    }

Printing the tree

There is nothing complicated here:

    void printTree(int[][] tree, int n) {
        System.out.println("The edges of the tree are:");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (tree[i][j] == 1) System.out.println("{" + i + "," + j + "}");
            }
        }
    }
Quick test for the code

We can easlly see that the code is fine:

public static void main(String[] args) {
        CayleyAlgorithm cayleyAlgorithm = new CayleyAlgorithm();

        int[] f = {4,3,1,0,2,1,7,3};
        cayleyAlgorithm.treeFromFunction(f);
       
    }

The output is:

L = 4, R = 2
The edges of the tree are:
{0,2}
{1,0}
{3,1}
{4,3}
{5,1}
{6,7}
{7,3}

You can verify it yourself and see if you got the same tree!

If you wish, you are welcome to download the file and test it a little, modify it or whatever you wanna do with. You can get it in this link:

https://github.com/TairGalili/Graph-Theory-Posts.git

Summary

We’ve seen in this post one of the most famous theorems in graph theory and saw how we can implement a proof into a code. Now if you are bored or something like that, you can generate a random function from [n] to itself to get a tree! In the next post I want to generalize Cayley’s theorem, and find out how many spanning trees are there in any graph, and I warn in advance – prepare yourself for some linear algebra!

One thought on “Cayley’s formula

Leave a Reply

%d bloggers like this: