5

私は現在、Bellman-Ford アルゴリズムを実装するための宿題に取り組んでいます。これまでのところ、提供されたグラフを読み込んで、それをベクトルに配置し (1 次元ベクトルを使用して行優先順序で 2 次元ベクトルを表す)、行列として使用することができました。エッジの重みを追跡する構造体、無限大 (エッジが存在しない) かどうかを示すブール値、および前のノードを追跡するための変数を使用しています。

私が困惑しているのは、実際にdangアルゴリズムを実装することです。http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithmから疑似コードを読みましたが、アルゴリズムの使用方法を理解するのに苦労しています。最初の 80 行は、ファイルを読み込み、ベクトルを初期化し、値をベクトルの適切な場所に投げ込んでいます。その下に、アルゴリズムの実装を開始したものがあります。いくつか具体的な質問があります。

1)私が見つけたアルゴリズムのすべての説明で、ノードの数 - 1 回アルゴリズムを実行します。私が見たこれのいくつかの実装では、i は 1 から始まる傾向があります。これはなぜですか? さらに、私の実装では、それはまだ意味がありますか?

2) さらにウィキペディアの擬似コードでは、u を開始頂点、v を終了頂点として、各エッジ u、v をチェックするように指示されています。私のマトリックスでは、理解できる限り、各行、列のペアの重み/値を確認し、より良いパスがあるかどうかを確認する必要があることを意味します。私はそれを正しく説明しているのか、それともこの点として理解しているのかさえわかりません。アドバイス/ガイダンス/ヒント/デモンストレーションをいただければ幸いです。講師のデモ入力を貼り付けたソースコードは以下のとおりです。

#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>

using namespace std;

struct graphNode
{
    int value; //Weight of the edge
    bool isInfinity; //Boolean to flag an edge as infinity
    int pred; //predecessor node
};

// Code for reading inputfile cribbed and modified from http://stackoverflow.com/questions/7651243/c-read-a-file-name-from-the-command-line-and-utilize-it-in-my-file
int main(int argc, char** argv) 
{
    ifstream input; // ifstream for the input
    string inFile = ""; //name of the input file
    int row; //Variable to keep track of what row we're inputting data for
    int col; //Variable to keep track of a column thingie, expand on this later
    int infinity = 99999999;
    int nodeCount; //Number of nodes from input file
    int edgeCount = 0; //Number of edges from the input file

    vector<graphNode> edgeList; //2D list of edges, order is a->b
    edgeList.push_back(graphNode());
    edgeList[0].value = 0;
    edgeList[0].isInfinity = false;
    edgeList[0].pred = -1;

    if( argc == 2 ) 
    {
        inFile = argv[1];
    }
    else 
    {
        cout << "Usage: ./a.out inputFile\n";
        return 1;
    }

    input.open(inFile.c_str()); // opening the provided file

    if(input.is_open()) // making sure the input is open
    {
        input >> nodeCount; //Grabbing the number of nodes from the first value of the file

        for(int i = 1; i < nodeCount*nodeCount; i++)
        {
            edgeList.push_back(graphNode());
            edgeList[i].value = infinity;
            edgeList[i].isInfinity = true;
            edgeList[i].pred = -1;
        }

        //Putting data from the file into the vector array
        while(!input.eof())
        {
            input >> row; //For each cycle through the list, we grab the first number on the line to get which x value (start vertex) we're working with
            while(input.peek() != '\n' && input.peek() != '\r' && !input.eof())
            {
                input >> col;
                input >> edgeList[((row-1)*nodeCount)+(col-1)].value;
                edgeList[((row-1)*nodeCount)+(col-1)].isInfinity = false;
                edgeList[((row-1)*nodeCount)+(col-1)].pred = row;
                edgeCount++;
            }

        }
        input.close(); //Closing our input file since we don't need it anymore
    }
    else
    {
        cout << "Error, something happened with the input." << endl;
        return 1;
    }

    //for(int i = 0; i < nodeCount - 1; i++)
    //{
        //for(int r = 0; r < nodeCount - 1; r++)
        //{
            //for(int c = 0; r < nodeCount - 1; c++)
            //{
                //if(r == c) continue;
                //if(edgeList[r][c].isInfinity) continue;
                //if(edgeList[i][r] + edgeList[r][c] < edgeList[c][i])
}

デモ入力:

10
3 6 4 9 0 7 8
8 5 3 7 3 4 -2
5 10 2 8 1 4 1
2 6 -3 1 3 7 1
1 10 -1 2 2 4 -2
10 9 -3 1 3 7 2 5 1
7 3 0 10 1 2 1 8 2
9 6 6 3 4 10 7
4 8 5 1 9 5 6
6 2 4 3 0 9 0
4

2 に答える 2

2

#1 では、最長パスが nodeCount-1 エッジを超えることができないように、ノード間のエッジを調べています。たとえば、nodeCount==1 の場合、エッジを調べる必要はありません。

#2 については、興味深いデータ構造があります。異なるものが必要なようです。あなたの「graphNode」は「graphEdge」と呼ばれるべきですが、「pred」変数はありません。2 つの新しい構造を宣言します。

vector<int> predecessor;
vector<int> distance;

それぞれのサイズは nodeCount です。ウィキペディアに表示される「w」は、edgeList です。

コメントアウトした最後のセクションでは、外側のループが nodeCount 回反復しています。nodeCount-1 のはずですが、害はありません。ただし、2 次元として扱っている 1 次元の edgeList があるため、後でインデックスを作成することはできません。edgeList[(r-1)*nodeCount + c] を介して正しいエッジにアクセスできます。

これが答えとしてカウントされるかどうかはわかりませんが、それは始まりです.

于 2013-04-17T04:41:19.047 に答える
2

Bellman-Ford アルゴリズムに関する短いビデオをこちらでご覧ください。アルゴリズムをよりよく理解するのに役立つと思います: https://class.coursera.org/algo2-2012-001/lecture/preview

基本的に、Bellman-Ford の背後にある主なアイデアは次のとおりです。

s と t などの 2 つのノード間の最短経路を見つけるには、s から t に到達するための最短経路を反復的に見つけます。その後の各反復では、アルゴリズムがパスで前の反復よりも 1 つ多いエッジを使用できるようにします。

特定の反復 k で、アルゴリズムがパスで最大k 個のエッジを使用できるようになった場合、s と t の間の最短パスは次のいずれかになります。

  1. 正確にk 個のエッジを使用して改善するか、
  2. 前回の反復で見つかった値と同じ値を使用します。つまり、最大で(k - 1) 個のエッジを使用します。

したがって、特定の反復 k では、次のようになります。

d[ k ][ t ] を s からノード t までの距離とし、最大 k 個のエッジを使用します。それで:

d[ k ][ t ] = min{ 
d[k - 1][ t ], # Case 2
for all edges (u, t) in graph min d[k - 1][ u ] + c[ u ][ t ] # Case 1
}

ここで、上記の min{ ., .} 式の 2 番目の部分は、s から最終目的地 t の任意の隣接 u までの最短経路を単純に見つけ、u から t に到達するエッジ c[ u ][ t ] のコストを追加します。となり、正確にk 個のエッジが必要になります。

したがって、擬似コードは次のようになります。

d[s][0] = 0 # The cost from s to itself requiring zero edges is 0.
d[u][0] = INFINITY for all other nodes other than s (i.e. you cannot reach them with no edges).

Let n = number of nodes in graph
for k = 1 to n - 1: # Allow one more edge in path in each iteration
  for every edge (u, v) in edges of graph: # Check whether can get a better cost to v using k edges by going through node u or k - 1 edges is enough.
    d[ v ][ k ] = min{ d[k - 1][ v ], d[k - 1][ u ] + c[ u ][ v ] }

質問のパート 1 に答えるには、方程式の外側のループがエッジの数だけループすることを考慮してください。1 から n - 1 まで増加します。ここで、n はグラフ内のノードの数です。最大 n - 1 である理由は、パスに含めることができるエッジの最大数が (n - 1) エッジであるためです (つまり、n 個のノードすべてを接続して s から t に到達し、n - 1 個のエッジを使用することになります)。それらの間の)。

質問のパート 2 に答えるには、すべての宛先ノードへの着信ノードを検討し、k - 1 エッジを使用してそれらのノードの 1 つへのパスに加えて、そのノードから宛先ノードまでのコストが以前よりも少ないかどうかを確認するだけで済みます。上で説明したように。

最後に、負のサイクル (wiki コードの最後の部分) をチェックするために、アルゴリズムをもう 1 回繰り返し実行します。どのパスも最大で n - 1 個のエッジを使用できることがわかっています。それ以上は冗長になります。したがって、エッジをもう 1 つ使用できるようにしたときにパスのコストが減少する場合は、負のサイクルが含まれている必要があります。これがコストを削減できる唯一の方法だからです。非負のサイクルは、より多くのエッジを使用するため、同じかそれ以上のコストが発生します。

上記のリンクにある Tim Roughgarden のビデオを見ることを強くお勧めします。彼の扱いはウィキペディアの疑似コードとは少し異なりますが、考え方は本質的に同じです。

于 2013-04-17T05:14:22.360 に答える