15

I'm wondering, if Dijkstra's algorithm will work properly when there is more than one direct connection in an undirected graph.

E.g.:

enter image description here

I want to use Dijkstra to find the fastest path but, there's an additional condition. Sum of all additional_data on the edges can't be >= x. So, if it came out that edge with weight: 3 was wrong to use, my program would try with the 2nd edge.

edit: My task is to find the fastest path, under the additional condition that the sum of additional_data from the edges can not be higher than x. Could you tell me how to handle this problem?

edit2: (setting up on a bounty)

I've been researching internet alot untill I've found this link. There's an explanation of how to do the thing I'm asking for. (Upper-Intermediate acapite)

I'm trying to use it somehow for 2 days now but I'm worried I do not understand this algorithm correctly. I'd like to ask some of you to help me with this problem, by explaining me a little more on example (few first steps). Here's the example:

enter image description here

Patryk
  • 3,042
  • 11
  • 41
  • 83
  • 9
    Parallel edges won't break Dijkstra, but the additional condition will. – John Dvorak Jan 06 '13 at 16:39
  • 2
    It is an exact duplicate, but the original question has no solution - only a statement that a solutions exist elsewhere (and that 815 people at SPOJ have found one). – LSerni Jan 06 '13 at 18:30
  • 2
    This is *not* an exact duplicate -- the other question makes no mention at all of even the possibility that there could be two or more paths between a pair of nodes, nor the extra condition attached to hem. – Jerry Coffin Jan 11 '13 at 20:33
  • This problem is np-complete, but if `sum(additional_data)` isn't bounded fairly low, you will want to look into approximating algorithms – Thomas Ahle Jan 17 '13 at 11:49

8 Answers8

8

I think you can modify Dijkstra's algorithm to handle this. Dijkstra's algorithm basically works by incrementally building a table listing the shortest path to every node. You would instead build a table listing the shortest path to every node at a given cost. Or rather, at a given cost or less, ie on a given budget.

More formally, you can transform your original graph into another graph, and then apply Dijkstra to that graph. Assuming that the additional_data cost is always an integer, the transformation is:

  1. Take every original node n and create a set of nodes (n, c) for every integer value of c from 0 up to the value of the budget (the maximum sum of additional_data that you can tolerate)
  2. Take every original connection n1 -> n2 with weight w and additional_data a, and create a set of connections from every node (n1, c) to the node (n2, c+a), each with weight w

The nodes in the original graph model positions in space. The nodes in the transformed graph model positions in a state space, where the state variables are position, and the amount of the budget spent so far.

If you want to get from A to Z with a budget of x, then you then simply use Dijkstra's algorithm to find a route from (A, 0) to one of the nodes (Z, c <= x).

EDIT: I have implemented the modified Dijkstra's algorithm: https://bitbucket.org/twic/roadsproblem. The crux of it is in src/solver.py.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • 1
    To be honest, I don't really get what you mean. Let's say I've max_sum = 200. Do I need to transform every node into set of 200 nodes in a transformed graph? – Patryk Jan 06 '13 at 18:42
  • 1
    Yes. Well, no. From 'more formally', i'm describing something simple that would work, which makes it easy to quasi-prove that what you want to do is possible. I wouldn't actually implement it like that. Rather, i'd do what i said in the first paragraph, which i realise i have not explained at all clearly, but which amounts to building that transformed graph lazily. – Tom Anderson Jan 06 '13 at 18:46
  • 1
    I am happy to explain this in more detail, perhaps with some code, but i have to go out for dinner. Perhaps later! – Tom Anderson Jan 06 '13 at 18:46
  • Hope to hear from you then :P – Patryk Jan 06 '13 at 18:57
  • I think there is Java code to implement the lazy version of Tom's algorithm at http://www.java3z.com/cwbwebhome/article/article17/acm862.html (the second program on this page). – Peter de Rivaz Jan 06 '13 at 20:22
  • They are using dfs. I'd like to know if theres a way to use Dijkstra on that. – Patryk Jan 07 '13 at 10:02
  • @TomAnderson, please tell me what do you think. Will this algorithm simply check every possible way from node a to node b at specified cost? Is that how it's gonna work? – Patryk Jan 12 '13 at 21:00
  • 1
    @Patryk: See edit. Really, the only modification from Dijkstra's algorithm is that instead of pursuing routes if they are shorter than the existing one, you pursue them if they are either shorter or cheaper. – Tom Anderson Jan 15 '13 at 23:28
5

Here is an explanation of how the algorithm you have found will handle your example problem.

The problem is to find the shortest path between node one and node four with the extra condition that the accumulated cost along the way should not be more than 7.

The solution we want to find is to first go from node one to node node two, a distance of 190 and at a cost of 4. And then go from node two to node four using the path of distance 195 and cost 3. In total the path has a distance of 385and a cost of 7.

Step 1

So how does the algorithm find this? The first step is to set up the matrix minArray(i,j) just like you have done. The element (i,j) of the array holds the distance you must travel to get to node j with exactly i money remaining.

Original array.

Starting out there are no visited elements and since we are starting at node one with 7 "monies" the top left element is set to zero. The empty spaces in the table above correspond to values that are set to infinity in the array.

Step 2

Next, we find the lowest value of the array, this is the zero at position (remaining money, node) = (7,1). This element is set to visited (the state of an element is kept track of using a matrix visitedArray of the same size as minArray) which means that we have selected node one. Now all nodes that connect to node one are updated with values by traversing the corresponding edges.

Array one.

Here minArray(6,3) = 191 which means that we have gone a distance of 191 to get to node three and the money we have left is 6 since we have payed a cost of 1 to get there. In the same way minArray(3,2) is set to 190. The red square marks that element (7,1) is visited.

Step 3

Now we again find the lowest unvisited element (which is minArray(3,2) = 190), set it to visited and update all elements that connect to it. This means that the distance is accumulated and the remaining money is calculated by subtracting the cost from the current value.

Array two.

Note that it is too expensive to go back to node one from node two.

Step 4

The next step, selecting minArray(6,3) = 191 looks like this.

Array three.

Step 5

Three steps later the array looks like this. Here the two elements that equal 382 and the one that equals 383 have been selected. Note that the value of an element is only updated if it is an improvement of (i.e. lower than) the current value.

Array four.

Step 6

The array continues to fill up until all elements are either visited or still have infinite value.

Array 5.

Final step

The final step is to find the total distance by finding the lowest value of column four. We can see that the minimal value, minArray(0,4) = 385 corresponds to the correct solution.

Note: If all values of column four would have been infinite, it would mean that there is no valid solution. The algorithm also specifies that if there are multiple values of equal and minimal distance in column four, the cheapest one is selected.

user1884905
  • 3,137
  • 1
  • 22
  • 22
1

Your additional condition makes the problem a lot harder. Looking at it, I think the only thing you can do, is find out all possible paths between the source and the target, sort them by total edge weight, and then check one by one if your additional condition holds.

However, the problem of finding all possible paths between two vertices, is NP-Hard. A slightly modified version of DFS might be able to do the trick, but probably not in all cases.

kokx
  • 1,706
  • 13
  • 19
1

I do not think Dijkstra's algorithm is a good solution to this problem since the distance needed is not only the source node and destination. Here is a solution based upon A* search algorithm.\

First, perform a FolydWarshall based on weight and then based on additional_data to get the least weight and least additional_data for each node pair in the graph.

  FloydWarshall(Weights);
  FloydWarshall(Additional_datas);

Second, we perform a A* search based on priority queue with element like following structure(Use c code as example.) The priority queue will automatically get the weights_sum least in all the candidates. weights_expected is the best guess of the path through current node to destination node while weights_now is current weight

  struct NODE
    {
        int node;
        int weights_expected;
            int weights_now;
        int additional_datas_now;
            bool visited;
    };
    bool operator < (const NODE &A,const NODE &B)
    {
        return A.weights_expected>B.weights_expected || (A.weights_expected==B.weights_expected && 
   A.additional_datas_now>B.additional_datas_now);
    }

In A* search algorithm,

1) we first put the source node into priority queue. 
  2) while Priority Queue is not empty:
        Set **A** equal to the head of priority queue and pop out the head of priority queue. 
        A.visited=True;
        if A is the destination node **Dest**, **return** A.weights_expected. 
        For each neighbors **B** of node **A**, 
          if A.visited==False **and** A.additional_datas_sum+|AB|.additional_data+Additional_datas[B][Dest]<=x, 
               1) B.additional_datas_now=A.additional_datas_now+|AB|.additional_data;    
               2) B.weights_now=A.weights_now+|AB|.weight;
               3) B.weights_expected=B.weights_now+Weights[B][Dest];
               3) push node B into priority Queue. 
   3) Print "Do not find a proper path" //if code came to here, that means the if in 2) do not return a value. 

A* search will be still NP hard since in worst case it has to search each possible path. However it will be much faster than a simple DFS search and perform lots of search path cuts.

1

You could make a copy of the node with 0 cost between them that adds the 2nd possible path.

Like so (pseudocode)

Node 1____
|         |
|path1    |
|cost=3   |path2 cost=5
|         |
Node 2____/

becomes this:

Node 1____cost=0____Node 1a
|         path 1a     |
|path1                |path2
|cost=3               |cost=5
|                     |
Node 2________________/

Not sure if this will work, but it's an idea.

Codeman
  • 12,157
  • 10
  • 53
  • 91
1

The additional condition will break Dijkstra. Think of it like this: if you have a path A->B in a graph and an edge B->C in a graph, then the shortest path A->C that involves B is surely the minimum path of A->B->C. In your case this condition doesn't hold, because while A->B and B->C might be valid, A->B->C might not be valid.

Okay, this is the point where you grab a piece of paper and try this.

If you look at your graph, and assuming you want to go from (1) to (4), notice how you can eliminate (3) by introducing the following edges:

  • (1)->(4), [390, 2]
  • (1)->(2), [383, 3]
  • (2)->(4), [391, 3]

Once you have eliminated the all edges but a straight line, the problem becomes somewhat easier: for each node you can keep track of how much [distance, additional] it will cost to reach the goal. You don't have to store additional > max or 'remaining additional' < 0, since that's not a viable solution. Also, if you have multiple distances for equal additional, only the minimal distance should be kept.

The best solution is now the one with the minimal distance in the last node (or the first, depending on how you ordered it). If down the road, you kept pointers on how you got there (e.g. if you update the value in the matrix also store the element that made it change), you can backtrack the path as well.

Here you should note that you can do the same when the problem was in the non-eliminated form with the matrix you suggest: x-axis as nodes, y-axis as 'list per node'.

That should do it.

atlaste
  • 30,418
  • 3
  • 57
  • 87
1

you can use the bellman-ford algorithm with the assumption that your addittional-data is the number of edges parameter in the bellman-ford algorithm.

Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
1

This problem is NP-complete. No algorithm is more efficient than the one explained by multiple people (Tom Anderson, user1884905).

Proof: By reducing of subset-sum for non-negative numbers.

Take an instance A of subset-sum (N numbers). Construct a graph, where there are N+1 nodes. For nodes i and i+1 , make 2 paths , one with weight=0, additional_data=A[i], another with weight=A[i], additional_data=0. Choose x(the limit for sum of additional_data).

Observe that the algorithm must minimize sum of weights, so it will also maximize sum of additional_data. So the paths of the first kind chosen will be the paths associated with numbers in the result of the subset-sum problem.

maniek
  • 7,087
  • 2
  • 20
  • 43