Collatz Conjecture

The challenge: Implement an an algorithm that simulates all numbers reaching the collatz conjecture using Dynamic Programming.

Take any positive integer n. If n is even, divide it by 2 to get n / 2. If n is odd, multiply it by 3 and add 1 to obtain 3n + 1. Repeat the process indefinitely. The conjecture is that no matter what number you start with, you will always eventually reach 1.

https://www.wikiwand.com/en/Collatz_conjecture

class Node {
public:
    Node(int val, int w) : connected_to(val), weight(w) {
        next = nullptr;
    }
    int connected_to;
    int weight; // weight of edge
    Node *next;
};

class LinkedList_edges {
public:
    LinkedList_edges() {
        front = end = nullptr;
    }

    void insert(int vertex, int weight) {
        Node *newNode = new Node(vertex, weight);

        if (isEmpty()) {
            front = end = newNode;
        }
        else {
            end->next = newNode;
            end = newNode;
        }
    }

    void traverse() {
        Node *temp = front;
        while (temp != nullptr) {
            cout << temp->connected_to << " -> ";
            temp = temp->next;
        }
        cout << endl;
    }


    // tranversing linked list to see if element exists
    bool exists(int number) {
        Node *temp = front;
        while (temp != nullptr) {
            if (number == temp->connected_to) {
                return true;
            }
            temp = temp->next;
        }
        return false;
    }

private:
    Node *front, *end;

    bool isEmpty() {
        return front == nullptr;
    }
};

// simulating friend network
class Graph {
public:
    Graph(bool dir) : directed(dir), n_verticies(0), n_edges(0) {
    }

    void addNumber(int number) {
        // error checking
        if (numberExists(number)) {
            cout << "number already exists" << endl;
            return;
        }

        // create new vertex
        LinkedList_edges *newnumber = new LinkedList_edges();
        // add to network
        graph.insert({ { number },{ newnumber } });
        ++n_verticies;
    }

    void removenumber(int number) {
        // error checking
        if (!numberExists(number) || graph.empty()) {
            cout << "number does not exist" << endl;
            return;
        }

        // removing a number will remove all connections
        // remove linked list, to avoid memory link
        delete graph.find(number)->second;
        graph.erase(number);
        --n_verticies;
    }

    void connectnumber(int source, int dest) {
        // error checking
        if (!numberExists(dest) || !numberExists(source)) {
            cout << "number(s) does not exists" << endl;
            return;
        }

        // are already friends
        if (graph.find(source)->second->exists(dest)) {
            cout << "number A is already friends with number B" << endl;
            return;
        }

        // add to network by adding to the linked list of friend
        const int weight = 1;
        graph.find(source)->second->insert(dest, weight);
        ++n_edges;
    }

    // check if number exists in the graph
    bool findNumber(int number) {
        if (graph.empty()) return false;
        // side note: This graph traversal is very slow
        // because we look at a visited node more than once


        // need to do a full graph traversal
        // I want to be able to tranverse in descending order
        // because then ill save time looking down the tree.
        // for each number in graph

        for (std::map<int, LinkedList_edges*>::reverse_iterator iter = graph.rbegin(); iter != graph.rend(); ++iter) {
            // first check all the the graph node itself
            if (iter->first == number) return true;

            // then check all its connections to see if the number exists in the graph
            if (iter->second->exists(number)) {
                return true;
            }
        }

        // exhausted all search
        return false;
    }


    void traverse() {
        for (auto& i : graph) {
            cout << i.first << ": ";
            graph.find(i.first)->second->traverse();
        }
    }

    int getNumVert() { return n_verticies; }
    int getNumEdges() { return n_edges; }

private:
    bool directed;
    int n_verticies, n_edges;

    // changed from unordered->map, because
    // when I iterate, I want to be able to
    // start from the largest number, and
    // iterate down the graph
    map<int, LinkedList_edges*> graph;

    bool numberExists(int number) {
        if (graph.find(number) == graph.end()) {
            return false;
        }
        return true;
    }
};


int main()
{
    Graph *number_network = new Graph(true);

    vector<int> sequence;

    // building the collatz graph
    for (int i = 2; i < 5000; i++) {
        // if the number exists in the network, then
        // we dont have to look further down the tree
        // otherwise we take that number, and follow the conjecture

        int newNum = i;
        int newNumCopy = newNum;
        bool found = number_network->findNumber(newNum);
        cout << "New Sequence: " << endl << endl;
        while (!found && newNum != 1) {
            cout << "Searching for number: " << newNum << endl;
            sequence.push_back(newNum);
            //pause();
            if (newNum % 2 == 0)
                newNum /= 2;
            else 
                newNum = (3 * newNum) + 1;

            found = number_network->findNumber(newNum);
        }
        sequence.push_back(newNum);

        // once newNum has proved to converge to 1, then
        // we can insert it's 'sequence' into the graph network

        // the number may have also been found, which we then must
        // insert the sequence of numbers that have not been found
        // and connect that sequence the number that has been found

        // in both cases, the algorithm is the same

        cout << newNumCopy << " has converged to " << newNum << endl;
        //pause();
        int src, dest;
        src = sequence.at(0);
        cout << "adding " << src << " to network" << endl;
        number_network->addNumber(src);

        for (int i = 0; i < sequence.size() - 1; i++) {
            src = sequence.at(i);
            dest = sequence.at(i + 1);

            number_network->addNumber(dest);
            cout << "adding " << dest << " to network" << endl;
            number_network->connectnumber(src, dest);
            cout << "connecting " << src << " -> " << dest << " to network" << endl;
            // 1 2
            // 1 -> 2
            // 1 -> 2 3
            // 1 -> 2 -> 3
        }
        sequence.clear();
        // look at the next number
    }
}

Last updated