Home Ian's Blog

Collatz Graphs

September 22, 2015

The Collatz conjecture is one of my favorite things from math. It says that if you take any number and repeatedly divide it by two when even, and multiply by 3 and add one when odd, that you will eventually reach 1. Despite appearing simple, this conjecture has never been proven – though it has been shown to be true for every number tested so far.

In one XKCD comic, the sequence of numbers is drawn as a graph with edges from each number in the sequence to the next. I found several versions of this graph, but wanted to see an even bigger one. So I wrote the following program which generates such a graph for all numbers up to the starting number given. Here is the program code (which uses the awesome graphviz utility for producing the images):


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* compute the next number in the Collatz sequence of value */
unsigned collatz(unsigned value) {
    if (value % 2) {
        return 3 * value + 1;
    } else {
        return value / 2;
    }
}

/* a node in the graph */
struct Node {
    unsigned value;
    unsigned next;;
};

/* test if there is a path from node s to node t in the graph */
int find(int s, int t, struct Node* graph, int n) {
    /* if node s IS node t, we've done it */
    if (s == t) {
        return 1;
    }

    /* if node s is bigger than n, then there is no path -
     * well there is one, but we wont' show it! */
    if (s > n) {
        return 0;
    }

    /* if the node is zero, then no path - this can happen
     * when we have already removed the node s would travel
     * trhough from the graph */
    if (s == 0) {
        return 0;
    }

    /* now we recurse on the link from s */
    return find(graph[s].next, t, graph, n);
}


int main(int argc, char** argv) {
    /* check they passed a number */
    if (argc != 2) {
        printf("usage: %s N\n", argv[0]);
        return -1;
    }
    unsigned n = atoi(argv[1]);
    if (n == 0) {
        printf("usage: %s N\n", argv[0]);
        return -1;
    }

    /* an array of nodes (one extra for n itself */
    struct Node graph[n + 1];
    graph[1].value = 1;
    graph[1].next = 0;

    /* for each number we are looking at */
    unsigned i;
    for (i = 2; i <= n; i++) {
        /* create the node for it */
        graph[i].value = i;

        /* compute its next collatz step */
        graph[i].next = collatz(i);
    }

    /* go through the graph and remove ones which don't reach 1, this
     * ensures the graph which is printed is complete */
    for (i = 2; i <= n; i++) {
        if (!find(i, 1, graph, n)) {
            graph[i].value = 0;
            graph[i].next = 0;
        }
    } 

    /* open the file and dump the preamble */
    FILE* f = fopen("collatz.gv", "w");
    fprintf(f, "digraph G {\n");

    /* dump all the nodes */
    for (i = 1; i<= n; i++) {
        /* print the node */
        if (graph[i].value) {
            fprintf(f, "    n%d[label=\"%d\"];\n", graph[i].value, graph[i].value);
        }

        /* print the link */
        if (graph[i].next) {
            fprintf(f, "    n%d -> n%d;\n", graph[i].value, graph[i].next);
        }
    }

    /* dump the postamble and close */
    fprintf(f, "}\n");
    fclose(f);

    /* generate the image */
    system("dot -Tpng collatz.gv -o collatz.png");

    return 0;
}

And here is the output for a size of 32 (the program only shows the portion of the graph which actually connects to node 1):

This link shows the very large (2447 * 4763) image for all numbers up to 1,000.

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.