0:03

So finally, we're going to take a look at the implications of having negative

weights in directed weighted graphs. So the first thing to notice is that the

easy solutions don't work at all. Dijkstra's algorithm just doesn't work in

the presence of, presence of negative weights.

So say, this weight from two to three is -nine.

What Dijkstra's algorithm will do is just immediately select vertex three and never

revisit that decision. Saying the shortest way to get from zero

to three is, is, Is to take this edge of three which has

weight two. But the actual shortest path goes over to

four and then over to one and down to two for a weight of ten.

And then a -nine makes it one. So there's a way to get from, zero to

three with a weight path weight just one. Because of the -nine.

But Dijkstra's algorithm won't find that. So that's not going to work and you might

think, well, why don't we just make all the weights positive by adding nine to

everything. Well, that's not going to work because a

longer path will have nine added to it a lot of times, so its just not any relation

between shortest paths in this graph and shortest paths in that graph.

So, those easy attempts, just don't work for dealing with, negative weights, in

general graphs. So the conclusion is we need some

different algorithm and the top logical sort one isn't going to work cuz the graph

might have a cycle in it, so there's no topological sort so we needs some

different algorithm. And the main thing to think about in terms

of weighted directed graphs, that might have cycles,

Is that you can have this situation called a negative cycle.

This is a graph, it all looks fine. It's just got this one negative edge from

five to four of weight -0.66. And these other ones are 0.37 + 0.28. So

that's +0.65. But if you go from five to four to seven

to five, Then the distance around that is -0.01.

And if we're trying to find, say a shortest path from zero to six, Well as

soon as you hit this cycle if you want really want the shortest path, what you

could do is spin around this well an infinite number of times.

You could make the path a short as you want, if you hit an infinite, if you hit a

negative cycle. So, negative cycles definitely can get in

the way of trying to solve the shortest path problem.

It make somewhat specified. So, the first thing is if there's no

negative cycles, then there's a shortest path tree, and if there's a shortest path

tree, there's no negative cycles assuming that you can actually get to the vertices.

So what we'll want is graphs that have no negative cycles and then we can work with

those. And is an algorithm an old algorithm known

as the Bellman-Ford algorithm that does the job.

It's only slightly more specific than the generic algorithm that we gave before.

It just says so if you have V vertices, for just V times all you do is go through

all the edges in the graph and relax each edge.

So you make V passes through relaxing all the edges in the graph.

And that's, an algorithm that finds shortest paths, in graphs that don't have

any negative cycles. If there's a negative cycle, it, you know,

we'll talk about what happens in a minute. So lets look at that one in terms of a

demo. So we'll just take the list of edges in

the graph it could be in any order, and we'll just relax them all.

4:36

So to start out, we, set the distance to the source zero and everybody else's

distance, infinity as usual. And then here's the list of edges.

So we'll relax In this case, we start out with the edges sort of in the order that

you'd get em' by walking through the whole graph so you get all the edges associated

with each vertex together. So relax zero, one, relax zero, four,

relax zero, seven, that's kind of the way Dijkstra would start out.

And then, we go ahead and relax the edges that, come out from one, so one, two takes

to, two for the first time, one, three takes us to three for the first time.

One, seven is no help so we ignore it. Now we relax.

Sorry, went too far. Two, three and two, six and those, that

one takes us to six for the first time. And now three, six that does not give us a

better way to six so we ignore it. Now we go to four, five that takes us to

five for the first time. Four, six well we could get to four and

nine and twenty to six so that's no help. Four, five that's no help.

Now we do the ones from five, five, two that's going to give a better way to two

so we've improved that. And five, six is going to give a better

way to six. Now, seven, five that's no help.

And seven, two is no help. So we've completed the first paths.

And now we're just gonna go through and relax all the edges again.

Now a lot of them are really just what we did before so they're not going to improve

anything, like the ones at the beginning are not gonna improve anything.

But before too long so still no improvement.

But here, now the second time we relax two, three we have it gives us a better

way to get to three. In, for the first path that didn't help us

but the second path we relax two, three to get us to three and seventeen.

Now that's going to change things for three, If we had already been through

three it wouldn't, wouldn't matter. In this, in this, we'd take another path

to deal with, but in this case, we're going to be coming through three later.

And, there's another, two to six gets better.

Then three to six well, two to six beat it so it doesn't help.

And now I think, there's nothing else that helps in this example.

7:23

And in fact, there's no further changes. Once we have the shortest path stream,

which, we know from this example, because it's similar to one we did before then

there's going to be no, no changes to it. You're not going to find any edges that

successfully relax. And so, go through and, try them all, and

in the end we have a shortest path stream. So that is an example of a Bellman-Ford

algorithm, on a simple, simple graph. Here's the visualization of the

Bellman-Ford algorithm running on a bigger graph.

And here's what it looks like after four passes, seven, ten, thirteen.

And this graph has 100 something vertices. And it finds the tree in a relatively

small number of passes. And we'll talk about the performance in a

second of. One thing is to be sure that the algorithm

works. And there's a couple of ways to prove it.

And again, the reason the proof has to be done with care is that there could be

edges with negative weights but no negative cycles.

The idea of the proof is that after you've done the i for pass, you've found the

shortest path containing at most i edges. And, and we'll leave that proof for the

book. Now there is a couple of ways to improve

the Bellman-Ford algorithm in practice. And, the most important one is that if you

didn't change the distance to a vertex during one pass,

Then you don't have to worry about it in the next pass.

You don't have to worry about it, it's edges in the next pass.

And so, what we do in practice, is if you just maintain a queue of the vertices that

we found shorter paths to, in each path. We want to make sure that we keep, at

most, one copy of each vertex on the queue.

Otherwise, you could wind up with situations where, the, queue, size of the

queue, blows up. But that's easy to do, with, a vertex

index array to keep track of that. And so, in the worst case, you could have

all the vertices on the queue for, And then therefore all the edges relaxed, in

every one of the V passes. But in practice not too many vertices

really get relaxed. So, this is a, a quick summary of the

algorithms that we've looked for, for, shortest paths, And, we didn't do the code

for Bellman-Ford. We've done enough code today.

It's quite simple code that you'll find in the book over on the book site.

So, probably the simplest algorithm is when there are no directed cycles.

And that's when we just topologically sort the vertices and then go through that list

and relax every edge. So that's linear time.

You relax all the edges in the graph and that's it.

And that works even if there are negative weights.

If there's no negative weights but there maybe cycles, then Dijkstra's algorithm

works. And then we looked at E log V algorithms

which are slightly faster depending on what kind of heap you use.

The Bellman-Ford algorithm which works with negative weights as long as it's no

negative cycles it's if, if you do the q you can get the in, in practice it turns

out to be linear time for the times of graphs that arise in practice.

Although in principle the worst case it could be E times V which is much to slow.

So, directed cycles make the problem harder.

You need a Dijkstra instead of top logical sort.

Negative weights definitely make the problem harder.

Because you might need, to, get the worst case of Bellman-Ford.

And negative cycles, in the presence of negative cycles, it actually makes the

problem intractable. And we'll talk about that a little bit at

the end of the course. One thing that you can do with the

Bellman-Ford algorithm is to at least find, find out if there's a negative

cycle. In one practical reason to do that is

maybe it has something to do with something else specified in the problem.

And so if you can detect a negative cycle and deal with it that would be useful.

And we'll look at another important practical reason for finding negative

cycles in just a second. So anyway, since its a useful thing to do

we're going to add two methods to the API. And that is does the graph have a negative

cycle and in an interval? If it does have a negative cycle please

give it to me. So for this graph, it would return true.

And then it would give an iterable that would have these three edges that give me

the negative cycle. So, the, observation or the way to find a

negative cycle is to realize that what will happen with a Bellman-Ford algorithm

if there's a negative cycle is that it'll every, every path through, it'll basically

go around the cycle. Well not every pass in the, in the whole

length in the cycle. It'll get stuck in a loop going around the

cycle depending on the order of vertices. And it'll update and just get stuck going

around the, around the cycle. So by testing what happens after

Bellman-Ford is done, even if there's negative cycles present, we can find the

negative cycles and that, in fact, If some vertex is updated in the last

phase of the Bellman-Ford algorithm then there's a negative cycle.

And not only that. Edge 2v is the last edge, on that cycle.

That's the way you got to v. And you can trace back to find the cycle.

So just run the Bellman-Ford algorithm and if some vertex gets, get updated, the last

time through it means there's a negative cycle.

In practice actually, you don't have to wait till the last phase.

You can check these two entries for negative cycles, more frequently.

But anyway, it's possible to find a negative cycle.

And let's look at an application. This is an application that it really

motives some people to understand the shortest paths to algorithms.

And the idea is currency exchange. And so these are typical numbers that you

might see in a newspaper table, or nowadays in an app on your mobile device

that gives the exchange rates for various currencies.

So to convert a dollar to euros using factor of points 0.741, I compute Euros

too, Great Britain pounds, 0.888, and so forth.

So this table is a table of exchange rates.

And the problem is, is there an arbitrage opportunity?

So what is an arbitrage. Well arbitrage is when just by making

exchanges according to the legal rates in the table you can make money.

So say we had a $1000. And then these are the going rates.

Well we can convert that $1000 into 741 Euros.

So now, if we take those 71 Euros and convert them into Canadian dollars it

works out that we get 1,012.206 Canadian dollars.

And if we take those Canadian dollars and convert them back to U.S.

Dollars, it works out that we have $1,007. Well that's arbitrage and that's

interesting. If we could go ahead and do for well let

say $10,000 then would make $70 on the deal or a $100,000 would make $700 or

maybe a million dollars would make $7,000 or maybe a billion would make $7,000,000.

No reason to stop there. With arbitrage opportunity you're making

money off the system. So of course there's intense interest in

looking for opportunities like this. Course in modern financial market.

It's there's many more things that you can trade than currencies.

And these tables are big and complex. And the market is suppose to take care of

eliminating these opportunities. But if you are using a computer and you've

got a fast algorithm for finding negative cycles in directed graphs then maybe you

can find the opportunity and take advantage of it before the market.

So let's look at how it works. What we do is again model the situation

with a graph. So we're going to have a vertex for every

currency. And then on the edge corresponding to

every transaction or every entry in the table so this is actually a complete

directed graph. And the weight is equal to the exchange

rate. And what we are trying to find is a

directed cycle whose product of edge weights is greater than one.

So that doesn't look like a shortest path problem although it's close.

And actually it's very close. To convert this to a shortest path problem

what we want to do is just take logs. If instead of using the exchange rate, we

take minus the log of the exchange rate. And then multiplying turns to addition for

looking for multiplying the exchange rates.

That's the same as summing the logs. And, we took minus log it means that what

we're looking for when we're trying to find products bigger than one.

We're looking for a directed cycle whose sum of edge weights is less than zero.

Find a directed cycle whose sum of edge weights is less than zero in a complete

digraph. That's the negative cycle detection

problem, and we just saw that, we can, do that with the, Bellman-Ford algorithm.

And again, in a huge, directed graph in a modern trading situation, that's an

extraordinarily valuable algorithm, and you can believe that you know,

There are people out there running these algorithms in order to detect and take

advantage of arbitrage opportunities. And if you don't have a fast algorithm, if

you're using a slow one it'll be gone before you can take advantage of it.

That's a fine example of the value of building efficient algorithm for a

practical problem. So here's our summary of short as fast

today. We've seen some great, classic algorithms

that are, important and extraordinarily useful.

First one is Dijkstra's algorithm which is, a fine, efficient workhorse algorithm

that you see used, every day. And it's a, a grass search algorithm that

is, similar to, Prim's, depth for search and rep for search that we've seen before

and we'll see again if the graphs are, have no cycles which is a situation that

arises in several important applications. We can do better than Dykstra's algorithm.

It's easier. And also, negative weights are no problem,

which enables us to solve scheduling problems in other examples If there's

negative weights and negative cycles we can detect negative cycles.

And if there aren't any negative cycles, we can find shortest paths.

In, the general problem is intractable, and we'll come back to that.

But the key point that I want people to remember from today's lecture is that

shortest path is our first real example of a general problem solving model where

there's a lot of important practical problems that we cast solve as shortest

path problems. Number one and number two we have fast

solutions to those problems, with these classic algorithms that we've talked about