4

I don't know if I should be asking this here or not, the question is about algorithm. Imagine you have an undirected graph. Edges have different values. Imagine that some vertice are "good" and some are "bad". Now I want to determine two good nodes, so that path between them is shortest possible (if path include bad nodes that's not a problem).

Giorgi Cercvadze
  • 403
  • 1
  • 7
  • 23
  • Dijkstra's algorithm finds the shortest path from a single node to every other node. So you just need to run Dijkstra for every good node. – user3386109 Apr 23 '18 at 18:52
  • Thought about that but nope, there are 10^6 nodes so doing it on every pair would las a lifetime. – Giorgi Cercvadze Apr 23 '18 at 18:54
  • 1
    An optimization is to keep track of the best path so far, and stop the algorithm early when all the path lengths exceed the best path. This is concept behind [branch and bound](https://en.wikipedia.org/wiki/Branch_and_bound). – user3386109 Apr 23 '18 at 18:57
  • Are edge values all > 0? – juvian Apr 23 '18 at 19:44
  • Is there a link with original description of problem with its constraints? – juvian Apr 23 '18 at 19:52
  • The Floyd-Warshall algorithm will calculate shortest paths between all nodes. If the graph is dense with good nodes, then this might work well enough. You can ignore paths that start or end on a bad node. – JerryM Apr 23 '18 at 23:20
  • 1
    I agree with @user3386109; You will have two constrains to stop the algorithm 1) if you reach a good node you do not pass it to another node. 2) if path exceeds the best recorded path so far. Updating the best recorded path will minimize your search every step. – Bassem Apr 24 '18 at 07:30

2 Answers2

1

What you want to do is start growing paths from all good nodes at once, and then stop shortly after you find that two meet. Then you have found your shortest path.

There is a subtle complication. Consider a triangle ABC. If the weights of A-B and B-C are both 2, and A-C is 3, you look at the edges A-B and B-C before A-C. Which means that you find the path A-B-C (weight 4) before A-C (weight 3). However in all such cases you will have seen that the edge exists before you found the first one.

Here is pseudocode.

node_path_info is is a dictionary of vertex to information about the path
upcoming is priority queue of vertices to consider next, sorted on .cost

initialize node_path_info and upcoming
for node in list of good nodes:
    upcoming.add( {
        "node": node,
        "node_from": None,
        "cost": 0,
        "good_node", node,
    } )

best_path_cost = None
best_middle1 = None
best_middle2 = None
while upcoming:
    current = upcoming.pop()
    if current.node in good_node_from:
        if current.good_node == good_node_from[current.node]:
            pass # We found a loop
        else:
            cost = current.cost + node_path_info[current.node].cost
            if best_path_cost is None or cost < best_path_cost < best_path_cost:
                best_path_cost = cost
                best_middle1 = current.node
                best_middle1 = current.node_from
    else:
        node_path_info[current.node] = current
        if best_path_cost is not None: # still looking for first path
            for (next_node, weight) in get_connected_weight(current.node):
                upcoming.add({
                    "node": next_node,
                    "node_from": current.node,
                    "cost": current.cost + weight,
                    "good_node", current.good_node,
                })

path1 = path from best_middle1 back
path2 = path from best_middle2 back
path1 + reversed(path2) is your answer.

In the worst case you will need to visit all edges twice. With a random connected graph and 2 good nodes, you will visit all edges connected to O(sqrt(n)) vertices.

btilly
  • 43,296
  • 3
  • 59
  • 88
  • I don't get the solution to the given complication. Can you tell it to me in details. I mean the triangle case. – Giorgi Cercvadze Apr 24 '18 at 18:53
  • @GeorgeTsertsvadze The problem happens when the middle link is high enough weight that we have not yet tried taking it when the first connecting path is found. However in this case the actual shortest path is already in `upcoming`, so we'll find it if we just process all of what is there. Which is why in pseudocode I stop adding to `upcoming` once we have a `best_path_cost`. – btilly Apr 24 '18 at 20:13
  • You can see this in the triangle case because the actual shortest path is added to `upcoming` right away. However the edge of length 3 is not processed until after the edges of length 2, so it isn't first found. – btilly Apr 24 '18 at 20:16
  • Thanks a lot sir. Just accepted that problem with this algorithm. – Giorgi Cercvadze Apr 24 '18 at 20:22
0

One approach is to add a source node that has a directed connection (with weight 0) to each good node.

Then run Dijkstra's algorithm to find the shortest path from the source node to every other node.

While running Dijkstra's algorithm, also keep track of which good node was the closest.

Then do a final pass over the edges A->B to find the cheapest value of "distance to good node from A" + "weight of edge" + "distance to good node from B", only including edges where the closest good node to A is not equal to the closest good node to B.

Peter de Rivaz
  • 33,126
  • 4
  • 46
  • 75
  • Couldn't you stop as soon as you hit another good node (after the first hop)? Op doesn't seem to care about anything beyond the single shortest distance between two good nodes. – Ian Mercer Apr 23 '18 at 20:19
  • @IanMercer Quite possibly - I was worried that the path might loop back to the good node it started from, but maybe there is a way of ensuring that can't happen? – Peter de Rivaz Apr 23 '18 at 20:55