Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do find the longest path in a cyclic Graph between two nodes?

I already solved most the questions posted here, all but the longest path one. I've read the Wikipedia article about longest paths and it seems any easy problem if the graph was acyclic, which mine is not.

How do I solve the problem then? Brute force, by checking all possible paths? How do I even begin to do that?

I know it's going to take A LOT on a Graph with ~18000. But I just want to develop it anyway, cause it's required for the project and I'll just test it and show it to the instructor on a smaller scale graph where the execution time is just a second or two.

At least I did all tasks required and I have a running proof of concept that it works but there's no better way on cyclic graphs. But I don't have clue where to start checking all these paths...

like image 732
rfgamaral Avatar asked Apr 20 '10 22:04

rfgamaral


People also ask

How do you find the maximum distance between two nodes on a graph?

For example: N = 8 ( Number of nodes) subset of nodes = [2,4,5,6] Graph: 7 | 6--1--2--8 | 3--4 | 5 Solution Maximum distance: Pairs (2,4) : 2-1-3-4 => distance 3 (6,5) : 6-1-3-5 => distance 3 max distance = 3+3 = 6 It's possible to form other pairs but max distance will always comes out to 6.

How do you find the longest simple path on a graph?

A path in a graph is called simple if it never passes through the same vertex twice. The Longest Simple Path (LSP) on a directed graph G = (V,E), a start vertex s ∈ V , and a goal vertex g ∈ V is a simple path from s to g such that no other simple path from s to g is longer.

Can BFS be used to find longest path?

We can find the longest path using two BFSs. The idea is based on the following fact: If we start BFS from any node x and find a node with the longest distance from x, it must be an endpoint of the longest path. It can be proved using contradiction.


1 Answers

The solution is to brute force it. You can do some optimizations to speed it up, some are trivial, some are very complicated. I doubt you can get it to work fast enough for 18 000 nodes on a desktop computer, and even if you can I have no idea how. Here's how the bruteforce works however.

Note: Dijkstra and any of the other shortest path algorithms will NOT work for this problem if you are interested in an exact answer.

Start at a root node *root*
Let D[i] = longest path from node *root* to node i. D[*root*] = 0, and the others are also 0.

void getLongestPath(node, currSum)
{
    if node is visited
        return;
    mark node as visited;

    if D[node] < currSum
        D[node] = currSum;

    for each child i of node do
        getLongestPath(i, currSum + EdgeWeight(i, node));

    mark node as not visited;
}

Let's run it by hand on this graph: 1 - 2 (4), 1 - 3 (100), 2 - 3 (5), 3 - 5 (200), 3 - 4 (7), 4 - 5 (1000)

Let the root be 1. We call getLongestPath(1, 0);
2 is marked as visited and getLongestPath(2, 4); is called
D[2] = 0 < currSum = 4 so D[2] = 4.
3 is marked as visited and getLongestPath(3, 4 + 5); is called
D[3] = 0 < currSum = 9 so D[3] = 9.
4 is marked as visited and getLongestPath(4, 9 + 7); is called
D[4] = 0 < currSum = 16 so D[4] = 16.
5 is marked as visited and getLongestPath(5, 16 + 1000); is called
D[5] = 0 < currSum = 1016 so D[5] = 1016.
getLongestPath(3, 1016 + 200); is called, but node 3 is marked as visited, so nothing happens.
Node 5 has no more child nodes, so the function marks 5 as not visited and backtracks to 4. The backtracking will happen until node 1 is hit, which will end up setting D[3] = 100 and updating more nodes.

Here's how it would look iteratively (not tested, just a basic idea):

Let st be a stack, the rest remains unchanged;
void getLongestPath(root)
{
    st.push(pair(root, 0));

    while st is not empty
    {
        topStack = st.top();
        if topStack.node is visited
            goto end;
        mark topStack.node as visited;

        if D[topStack.node] < topStack.sum
            D[topStack.node = topStack.sum;

        if topStack.node has a remaining child (*)
            st.push(pair(nextchild of topStack.node, topStack.sum + edge cost of topStack.node - nextchild)) 

        end:
        mark topStack.node as not visited
        st.pop();
    }
}

(*) - this is a bit of a problem - you have to keep a pointer to the next child for each node, since it can change between different iterations of the while loop and even reset itself (the pointer resets itself when you pop the topStack.node node off the stack, so make sure to reset it). This is easiest to implement on linked lists, however you should use either int[] lists or vector<int> lists, so as to be able to store the pointers and have random access, because you will need it. You can keep for example next[i] = next child of node i in its adjacency list and update that accordingly. You might have some edge cases and might need to different end: situations: a normal one and one that happens when you visit an already visited node, in which case the pointers don't need to be reset. Maybe move the visited condition before you decide to push something on the stack to avoid this.

See why I said you shouldn't bother? :)

like image 84
IVlad Avatar answered Sep 27 '22 22:09

IVlad