二维数组中的贝尔曼-福特算法

时间:2021-12-11 21:30:28

I've got a problem with applying a Bellman-Ford algorithm to 2D Array (not to graph)

我遇到了一个问题将Bellman-Ford算法应用到2D数组(不是图形)

Input array has m x n dimensions:

输入阵列具有mxn维数:

           s[1,1] s[1,2] ... s[1,n] -> Exit
           s[2,1] s[2,2] ... s[2,n]
           ...
 Entry ->  s[m,1] s[m,2] ... s[m,n]

And it is room-alike (each entry is a room with s[x,y] cost of enterance). Each room could have also a negative cost, and we have to find cheapest path from Entry to choosen room and to Exit.

这是一个房间一样的(每个入口都是一个有s[x,y]成本的房间)。每个房间也可能有一个负的成本,我们必须找到最便宜的路径从入口到选择房间再到出口。

For example, we've got this array of rooms and costs:

例如,我们有一系列的房间和费用:

1   5   6
2  -3   4
5   2  -8

And we want to walk over room [3,2], s[3,2] = 4. We are starting form 5 at [1,3] and must walk over [3,2] before we go to [3,3].

我们想要走过房间[3,2]s[3,2] = 4。我们从[1,3]开始,在[3,3]之前必须走[3,2]。

And my question is, what is the best way to implement it in Bellman-Ford algorithm? I know that Dijkstry algorithm will not work becouse of negative cost.

我的问题是,在Bellman-Ford算法中实现它的最佳方法是什么?我知道Dijkstry算法由于负成本而不能工作。

Is for each room from [0, maxHeight] and relax all neighbors correct? Like this:

每个房间都是[0,maxHeight],并且让所有的邻居都放松,对吗?是这样的:

   for (int i = height-1; i >= 0; --i) {
        for (int j = 0; j < width; ++j) {
            int x = i;
            int y = j;
            if (x > 0) // up
                Relax(x, y, x - 1, y);
            if (y + 1 < width) // right
                Relax(x, y, x, y + 1);
            if (y > 0) // left
                Relax(x, y, x, y - 1);
            if (x + 1 < height) // down
                Relax(x, y, x + 1, y);
        }
    }

But how can I then read a cost to choosen room and from room to exit?

但是我怎么能读到一个房间和从一个房间到另一个房间的费用呢?

1 个解决方案

#1


10  

If you know how to move on the graph from an array, you can scroll to additional condition paragraph. Read also next paragraph.

In fact, you can look at that building like on a graph.

如果您知道如何从数组中移动图形,您可以滚动到附加条件段落。读下一段。事实上,你可以像在图表上一样看那座建筑。

二维数组中的贝尔曼-福特算法 You can see like: (I forgot doors in second line, sorry.) 二维数组中的贝尔曼-福特算法

你可以看到:(我忘了第二排的门,对不起。)

So, how it is possible to be implement. Ignore for the moment additional condition (visit a particular vertex before leaving).
Weight function:
Let S[][] be an array of entry cost. Notice, that about weight of edge decides only vertex on the end. It has no matter if it's (1, 2) -> (1,3) or (2,3) -> (1, 3). Cost is defined by second vertex. so function may look like:

所以,如何实现它。暂时忽略附加条件(在离开之前访问一个特定的顶点)。权重函数:设S[][]为输入成本的数组。注意,关于边的权值只决定末端的顶点。无论它是(1,2)->(1,3)还是(2,3)->(1,3),成本由第二个顶点定义。函数是这样的

cost_type cost(vertex v, vertex w) {
    return S[w.y][w.x];
}
//As you can see, first argument is unnecessary.

Edges:
In fact you don't have to keep all edges in some array. You can calculate them in function every time you need. The neighbours for vertex (x, y) are (x+1, y), (x-1, y), (x, y+1), (x, y-1), if that nodes exist. You have to check it, but it's easy. (Check if new_x > 0 && new_x < max_x.) It may look like that:

边:事实上,你不必把所有的边都保存在某个数组中。你可以在每次需要的时候用函数计算它们。顶点(x, y)的邻边是(x+1, y), (x-1, y), (x, y+1), (x, y-1)你得检查一下,但很简单。(检查new_x > && new_x是否< max_x)它可能是这样的:

//Size of matrix is M x N
is_correct(vertex w) {
    if(w.y < 1 || w.y > M || w.x < 1 || w.x > N) {
        return INCORRECT;
    }
    return CORRECT;
}

Generating neighbours can look like:

生成邻域如下:

std::tie(x, y) = std::make_tuple(v.x, v.y);
for(vertex w : {{x+1, y}, {x-1, y}, {x, y+1}, {x, y-1}}) {
    if(is_correct(w) == CORRECT) {//CORRECT may be true
        relax(v, w);
    }
}

I believe, that it shouldn't take extra memory for four edges. If you don't know std::tie, look at cppreference. (Extra variables x, y take more memory, but I believe that it's more readable here. In your code it may not appear.)

我相信,四边不需要额外的记忆。如果你不知道std::tie,看看cppreference吧。(额外的变量x, y占用更多的内存,但我相信这里更容易读懂。在您的代码中,它可能不会出现。

Obviously you have to have other 2D array with distance and (if necessary) predecessor, but I think it's clear and I don't have to describe it.

显然,你必须有其他的二维数组,有距离和(如果有必要的话),但我认为很明显,我不需要描述它。

Additional condition:
You want to know cost from enter to exit, but you have to visit some vertex compulsory. Easiest way to calculate it is to calculate cost from enter to compulsory and from compulsory to exit. (There will be two separate calculations.) It will not change big O time. After that you can just add results.

附加条件:您希望知道从输入到输出的成本,但是您必须访问某个顶点。最简单的计算方法是计算成本从输入到强制,从强制到退出。(将有两个单独的计算。)时间不会改变什么。之后你可以添加结果。

You just have to guarantee that it's impossible to visit exit before compulsory. It's easy, you can just erase outgoing edges from exit by adding extra line in is_correct function, (Then vertex v will be necessary.) or in generating neighbours code fragment.

你只需要保证在强制之前不可能访问出口。很简单,你可以通过在is_correct函数中添加额外的行(那么顶点v将是必需的)或者在生成邻域代码片段中删除出站的边。

Now you can implement it basing on wikipedia. You have graph.

现在你可以在*上实现它。你有图。

Why you shouldn't listen?
Better way is to use Belman Ford Algorithm from other vertex. Notice, that if you know optimal path from A to B, you also know optimal path from B to A. Why? Always you have to pay for last vertex and you don't pay for first, so you can ignore costs of them. Rest is obvious.
Now, if you know that you want to know paths A->B and B->C, you can calculate B->A and B->C using one time BF from node B and reverse path B->A. It's over.
You just have to erase outgoing edges from entry and exit nodes.

你为什么不听?更好的方法是从其他顶点使用Belman Ford算法。注意,如果你知道A到B的最优路径,你也知道B到A的最优路径,为什么?你总是要为最后一个顶点付费而不是为第一个顶点付费,所以你可以忽略它们的成本。休息是显而易见的。现在,如果你想知道路径A->B和B->C,你可以计算B->A和B->C利用节点B的一次BF和反向路径B->A。这是结束了。你只需要擦除出入口和出口节点的边。

However, if you need very fast algorithm, you have to optimize that. But it is for another topic, I think. Also, it looks like no one is interested in hard optimization.
I can quickly add, just that small and easy optimization bases at that, that you can ignore relaxation from correspondingly distant vertices. In array you can calculate distance in easy way, so it's pleasant optimization.
I have not mentioned well know optimization, cause I believe that all of them are in a random course of the web.

但是,如果你需要非常快速的算法,你必须对它进行优化。但我认为这是另一个话题。而且,看起来没有人对硬优化感兴趣。我可以快速地添加,就是那个小而简单的优化基,你可以忽略相应的远点的松弛。在数组中,你可以用简单的方法计算距离,所以这是一个令人愉快的优化。我还没有提到大家都很了解的优化,因为我相信所有的优化都是在网络上随机进行的。

#1


10  

If you know how to move on the graph from an array, you can scroll to additional condition paragraph. Read also next paragraph.

In fact, you can look at that building like on a graph.

如果您知道如何从数组中移动图形,您可以滚动到附加条件段落。读下一段。事实上,你可以像在图表上一样看那座建筑。

二维数组中的贝尔曼-福特算法 You can see like: (I forgot doors in second line, sorry.) 二维数组中的贝尔曼-福特算法

你可以看到:(我忘了第二排的门,对不起。)

So, how it is possible to be implement. Ignore for the moment additional condition (visit a particular vertex before leaving).
Weight function:
Let S[][] be an array of entry cost. Notice, that about weight of edge decides only vertex on the end. It has no matter if it's (1, 2) -> (1,3) or (2,3) -> (1, 3). Cost is defined by second vertex. so function may look like:

所以,如何实现它。暂时忽略附加条件(在离开之前访问一个特定的顶点)。权重函数:设S[][]为输入成本的数组。注意,关于边的权值只决定末端的顶点。无论它是(1,2)->(1,3)还是(2,3)->(1,3),成本由第二个顶点定义。函数是这样的

cost_type cost(vertex v, vertex w) {
    return S[w.y][w.x];
}
//As you can see, first argument is unnecessary.

Edges:
In fact you don't have to keep all edges in some array. You can calculate them in function every time you need. The neighbours for vertex (x, y) are (x+1, y), (x-1, y), (x, y+1), (x, y-1), if that nodes exist. You have to check it, but it's easy. (Check if new_x > 0 && new_x < max_x.) It may look like that:

边:事实上,你不必把所有的边都保存在某个数组中。你可以在每次需要的时候用函数计算它们。顶点(x, y)的邻边是(x+1, y), (x-1, y), (x, y+1), (x, y-1)你得检查一下,但很简单。(检查new_x > && new_x是否< max_x)它可能是这样的:

//Size of matrix is M x N
is_correct(vertex w) {
    if(w.y < 1 || w.y > M || w.x < 1 || w.x > N) {
        return INCORRECT;
    }
    return CORRECT;
}

Generating neighbours can look like:

生成邻域如下:

std::tie(x, y) = std::make_tuple(v.x, v.y);
for(vertex w : {{x+1, y}, {x-1, y}, {x, y+1}, {x, y-1}}) {
    if(is_correct(w) == CORRECT) {//CORRECT may be true
        relax(v, w);
    }
}

I believe, that it shouldn't take extra memory for four edges. If you don't know std::tie, look at cppreference. (Extra variables x, y take more memory, but I believe that it's more readable here. In your code it may not appear.)

我相信,四边不需要额外的记忆。如果你不知道std::tie,看看cppreference吧。(额外的变量x, y占用更多的内存,但我相信这里更容易读懂。在您的代码中,它可能不会出现。

Obviously you have to have other 2D array with distance and (if necessary) predecessor, but I think it's clear and I don't have to describe it.

显然,你必须有其他的二维数组,有距离和(如果有必要的话),但我认为很明显,我不需要描述它。

Additional condition:
You want to know cost from enter to exit, but you have to visit some vertex compulsory. Easiest way to calculate it is to calculate cost from enter to compulsory and from compulsory to exit. (There will be two separate calculations.) It will not change big O time. After that you can just add results.

附加条件:您希望知道从输入到输出的成本,但是您必须访问某个顶点。最简单的计算方法是计算成本从输入到强制,从强制到退出。(将有两个单独的计算。)时间不会改变什么。之后你可以添加结果。

You just have to guarantee that it's impossible to visit exit before compulsory. It's easy, you can just erase outgoing edges from exit by adding extra line in is_correct function, (Then vertex v will be necessary.) or in generating neighbours code fragment.

你只需要保证在强制之前不可能访问出口。很简单,你可以通过在is_correct函数中添加额外的行(那么顶点v将是必需的)或者在生成邻域代码片段中删除出站的边。

Now you can implement it basing on wikipedia. You have graph.

现在你可以在*上实现它。你有图。

Why you shouldn't listen?
Better way is to use Belman Ford Algorithm from other vertex. Notice, that if you know optimal path from A to B, you also know optimal path from B to A. Why? Always you have to pay for last vertex and you don't pay for first, so you can ignore costs of them. Rest is obvious.
Now, if you know that you want to know paths A->B and B->C, you can calculate B->A and B->C using one time BF from node B and reverse path B->A. It's over.
You just have to erase outgoing edges from entry and exit nodes.

你为什么不听?更好的方法是从其他顶点使用Belman Ford算法。注意,如果你知道A到B的最优路径,你也知道B到A的最优路径,为什么?你总是要为最后一个顶点付费而不是为第一个顶点付费,所以你可以忽略它们的成本。休息是显而易见的。现在,如果你想知道路径A->B和B->C,你可以计算B->A和B->C利用节点B的一次BF和反向路径B->A。这是结束了。你只需要擦除出入口和出口节点的边。

However, if you need very fast algorithm, you have to optimize that. But it is for another topic, I think. Also, it looks like no one is interested in hard optimization.
I can quickly add, just that small and easy optimization bases at that, that you can ignore relaxation from correspondingly distant vertices. In array you can calculate distance in easy way, so it's pleasant optimization.
I have not mentioned well know optimization, cause I believe that all of them are in a random course of the web.

但是,如果你需要非常快速的算法,你必须对它进行优化。但我认为这是另一个话题。而且,看起来没有人对硬优化感兴趣。我可以快速地添加,就是那个小而简单的优化基,你可以忽略相应的远点的松弛。在数组中,你可以用简单的方法计算距离,所以这是一个令人愉快的优化。我还没有提到大家都很了解的优化,因为我相信所有的优化都是在网络上随机进行的。