Floyd-Warshall算法没有正确找到最短路径的长度

时间:2014-06-13 14:53:40

标签: c dynamic-programming floyd-warshall

这可能是一个很糟糕的问题,因为我的代表是如此之低,但我一直在寻找其他解决方案几个小时,我的代码看起来几乎与我遇到的工作解决方案相同。请不要忽视基于低代表的问题。

输出矩阵d [] []包含给定顶点对之间的最短路径的(不正确的)长度。已经使用了用于Python的networkx库中提供的解决方案。

作为摘录,提供了 n = 20 的结果。我没有打印出大于无穷大的路径(即99999),因为有溢出。

这就是图表的样子:


My Floyd-Warshall算法实现(C)

20  0   2
20  1   6
20  2   9
20  3   9
20  4   8
20  5   10
20  7   2
20  8   7
20  9   10
20  11  5
20  12  2
20  13  7
20  14  6
20  15  17
20  17  4
20  18  5

Networkx解决方案Floyd-Warshall算法(Python)

20  0   2
20  1   5
20  2   4
20  3   4
20  4   3
20  5   7
20  7   2
20  8   2
20  9   4
20  11  4
20  12  2
20  13  6
20  14  5
20  15  4
20  17  3
20  18  4
20  20  0

实现:

#include <time.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <limits.h>

#define INF 9999
#define min(a,b) (a>b)?b:a;

int n;
/*
* Method signatures
*/
void shortestPath(int matrix[][n]);

int main(){
    char buf[16], c;
    int i, j, weight, ret;

    /* Open file handler for file containing test data */
    FILE *file = fopen("eg2.txt", "r");
    if(file==NULL){
        puts("I/O error: cannot read input file");
        fclose(file);
        exit(1);
    }
    /* Get number of vertices in file */
    fscanf(file, "%d", &n);

    /* Initialise matrix of n*3 elements */
    int matrix[n][n];
    memset(matrix, INF, n*n*sizeof(int));

    while((ret = fscanf(file, "%d %d %d", &i, &j, &weight)) != EOF) {
        if(ret == 3){
            matrix[i][j]=weight;
        } else {
            printf("ERROR: retrieved %d values (expecting 3)\n", ret);
            break;
        }
    }
    fclose(file);

    /* Output matrix */
    for(i=0; i<n; i++){
        matrix[i][i]=0;
        for(j=0; j<n; j++){
            printf("%d  ", matrix[i][j]);
        }
        printf("\n");
    }
    shortestPath(matrix);
}
/*
* Implementation of the Floyd-Warshall path finding algorithm
*/
void shortestPath(int matrix[][n]){
    int d[n][n], k, i, j;

    /* Copy values from matrix[][] to d[][] */
    for(i=0; i<n; i++){
        for(j=0; j<n; j++){
            d[i][j] = matrix[i][j];
        }
    }
    for(k=0; k<n; k++){
        for(i=0; i<n; i++){
            for(j=0; j<n; j++){
                if (d[i][k] + d[k][j] < d[i][j]){
                    d[i][j] = d[i][k] + d[k][j];
                }
            }
        }
    }
    for(i=0; i<n; i++){
        for(j=0; j<n; j++){
            if((d[i][j]!=0)&&(d[i][j]<INF)){
                printf("%d\t%d\t%d\n", i, j, d[i][j]);
            }
        }
    }
}

测试客户端(Python)

#!/usr/bin/python2.7
try:
    import matplotlib.pyplot as plt
    from collections import defaultdict
    import networkx as nx
    import numpy as np
except:
    raise

nodes = defaultdict(dict)
with open('eg2.txt', 'r') as infile:
    for line in infile.readlines()[1:]:
        line = map(int, line.split())
        src = line[0]
        dst = line[1]
        weight = line[2] 
        nodes[src][dst]=weight

G = nx.Graph()

for i in nodes:
    for j in nodes[i]:
        G.add_edge(i, j, weight=nodes[i][j])

rs = nx.floyd_warshall(G)
for i in rs:
    for j in rs[i]:
        print "%d\t%d\t%d" % (i, j, rs[i][j])

pos = nx.shell_layout(G)
nx.draw(G, pos, node_size=500, node_color='orange', edge_color='blue', width=1)

plt.axis('off')
plt.show()

2 个答案:

答案 0 :(得分:1)

不要使用动态大小的数组(例如数组大小中的非常数n),它们可能无法按照您的想法运行。修复代码的一种简单方法:

#define MAXN 100
int n;
...
  int matrix[MAXN][MAXN];
  scanf("%d", &n);
  if (n < 1 || n > MAXN) abort();
...
void shortestPath(int matrix[][MAXN]) {

请在启用所有警告的情况下重新编译您的代码(例如gcc -W -Wall -Wextra -ansi),修复所有警告,并在问题中指出您的代码是否编译而不会发出任何警告。

答案 1 :(得分:0)

这是一个完整的解决方案。我使用@ pts建议使用固定数组,以及使用一对嵌套循环显式初始化数组的注释的建议。我还对算法的工作方式采取了一些自由 - 例如,可以选择定向或无向图 - 并展示如何包含一些中间输出以帮助调试。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INF 9999
#define MIN(a,b)((a)<(b))?(a):(b)
// uncomment the next line to make processing symmetrical
// i.e. undirected edges
// #define SYM

#define NMAX 20
int n;

void shortestPath(int m[NMAX][NMAX]);
void printMatrix(int m[NMAX][NMAX]);

// implementation of floyd-warshall algorithm
// with minimal error checking
// input file = series of nodes on graph in form
// start, end, length
// algorithm attempts to find shortest path between any connected nodes
// by repeatedly looking for an intermediate node that shortens the current distance
// graphs are directional - 3 4 5 does not imply 4 3 5
// this can be changed by uncommenting the #define SYM line above
// also, hard coded to have no more than 20 nodes - defined with NMAX above
// path to input file is hard coded as "eg2.txt"

int main(void) {
  int i, j, weight, ret;

// open input file:
  FILE *fp = fopen("eg2.txt", "r");
  if(fp == NULL) {
    printf("cannot read input file\n");
    exit(1);
  }
// read number of nodes in the graph:
  fscanf(fp, "%d", &n);
  if(n > NMAX) {
    printf("input too large\n");
    fclose(fp);
    exit(1);
  }
  printf("n is %d\n", n);

// generate matrix:
  int matrix[NMAX][NMAX];
  for(i=0; i<NMAX;i++)
    for(j=0; j<NMAX; j++)
      matrix[i][j] = INF;

  while( (ret = fscanf(fp, "%d %d %d", &i, &j, &weight)) != EOF) {
    if(ret == 3) {
      matrix[i][j] = weight;
#ifdef SYM
      matrix[j][i] = weight;
#endif
    }
  else printf("error reading input\n");
  }
  fclose(fp);

  printMatrix(matrix);
  shortestPath(matrix);
  printMatrix(matrix);

}
void printMatrix(int m[NMAX][NMAX]) {
  int i, j;
  for(i=0; i<n; i++) {
    for(j=0; j<n; j++) {
      if(m[i][j]==INF) printf("  - "); else printf("%3d ", m[i][j]);
    }
    printf("\n");
  }

}

void shortestPath(int d[NMAX][NMAX]) {
  int i, j, k, temp;
  // no need to make a copy of the matrix: operate on the original
  for(k=0; k<n; k++) {
    for(i=0; i<n-1; i++) {
      for(j=0; j<n; j++) {
        if(i==j) continue; // don't look for a path to yourself...
        if(d[i][k] == INF || d[k][j]==INF) continue; // no path if either edge does not exist
        if((temp = d[i][k] + d[k][j]) < d[i][j]) {
          d[i][j] = temp;
#ifdef SYM
          d[j][i] = temp;
#endif
          printf("from %d to %d is shorter via %d: %d + %d is %d\n", i, j, k, d[i][k], d[k][j], temp);
        }
      }
    }
  }
  for(i=0; i<n; i++) {
    for(j=0; j<n; j++) {
      if(d[i][j] < INF) printf("%2d %2d %3d\n", i, j, d[i][j]);
    }
  }
}

使用以下输入文件:

5
1 2 3
2 4 2
1 4 8
0 3 7
3 1 2
1 4 2
1 3 1
0 1 1

我得到了输出:

n is 5
  -   1   -   7   - 
  -   -   3   1   2 
  -   -   -   -   2 
  -   2   -   -   - 
  -   -   -   -   - 
from 0 to 2 is shorter via 1: 1 + 3 is 4
from 0 to 3 is shorter via 1: 1 + 1 is 2
from 0 to 4 is shorter via 1: 1 + 2 is 3
from 3 to 2 is shorter via 1: 2 + 3 is 5
from 3 to 4 is shorter via 1: 2 + 2 is 4
 0  1   1
 0  2   4
 0  3   2
 0  4   3
 1  2   3
 1  3   1
 1  4   2
 2  4   2
 3  1   2
 3  2   5
 3  4   4
  -   1   4   2   3 
  -   -   3   1   2 
  -   -   -   -   2 
  -   2   5   -   4 
  -   -   -   -   - 

奇怪的是,当我运行你的代码时(如上所述)它给了我相同的解决方案 - 尽管第一部分的输出非常清楚memset没有按预期工作:< / p>

0  1  252645135  7  252645135  
252645135  0  3  1  2  
252645135  252645135  0  252645135  2  
252645135  2  252645135  0  252645135  
252645135  252645135  252645135  252645135  0  
0   1   1
0   2   4
0   3   2
0   4   3
1   2   3
1   3   1
1   4   2
2   4   2
3   1   2
3   2   5
3   4   4

实际上,使用memset操作写入矩阵的数字为0x0F0F0F0F,小数为252645135。您可以通过查看the syntax of memset

了解原因
  

void *memset(void *str, int c, size_t n)
  参数
  str --这是指向要填充的内存块的指针。

     

c --这是要设置的值。该值作为int传递,但该函数使用此值的unsigned char转换填充内存块。
  n --这是要设置为该值的字节数。

并与9999的十六进制表示相结合,即

0x270F

int的“unsigned char转换”是以256为单位的数字,或者是最低有效字节。在这种情况下,最低有效字节是0x0F,这是写入(重复)到块中每个字节的值 - 因此值0x0F0F0F0F(在我的机器上,int是四个字节长。)

后记
最后 - 如果您想使用“任何大小的数组”,您可以将以下几个函数添加到您的程序中 - 并替换所示的函数签名。这是一种在C中创建可变大小的二D数组的“棘手”方法 - 实质上,当C遇到类型int**的指针时,它将取消引用两次。通过使这个指向指针的指针指向一块指向内存块的指针,你可以创建一个编译器可以理解的2D数组。

int **make2D(int r, int c) {
  int ii, **M;
  M = malloc(r * sizeof(int*) );
  M[0] = malloc( r * c * sizeof(int) );
  for(ii=1; ii<r; ii++) M[ii] = M[0] + ii * c * sizeof(int);
  return M;
}

void free2D(int** M) {
  free(M[0]);
  free(M);
}

现在用

生成矩阵
int **matrix;
matrix = make2D(n, n);

并将功能签名更改为

void shortestPath(int **m);
void printMatrix(int **m);

打电话给他们
shortestPath(matrix); // etc

要使一切正常工作,您必须在代码中进行一些其他调整(例如:当您分配的内存少于此时,不应尝试通过NMAX阵列为所有NMAX元素分配INF)。您可以尝试自己解决这个问题 - 但以防万一,这里是完整的代码。我做了另外一个改变 - 我将n作为一个全局变量去掉,并使其成为main的本地变量(并将其传递给需要它的各种例程)。这通常是一个很好的做法 - 将全局内容混合起来太容易了,所以只有当你真的别无选择时才使用它们。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INF 9999
#define MIN(a,b)((a)<(b))?(a):(b)
// uncomment the next line to make processing symmetrical
// i.e. undirected edges
// #define SYM

void shortestPath(int **m, int n);
void printMatrix(int **m, int n);

// create 2D matrix of arbitrary (variable) size
// using standard C:
int **make2D(int r, int c) {
  int ii, **M;
  M = malloc(r * sizeof(int*) );
  M[0] = malloc( r * c * sizeof(int) );
  for(ii=1; ii<r; ii++) M[ii] = M[0] + ii * c * sizeof(int);
  return M;
}

void free2D(int** M) {
  free(M[0]);
  free(M);
}

// implementation of floyd-warshall algorithm
// with minimal error checking
// input file = series of nodes on graph in form
// start, end, length
// algorithm attempts to find shortest path between any connected nodes
// by repeatedly looking for an intermediate node that shortens the current distance
// graphs are directional - 3 4 5 does not imply 4 3 5
// this can be changed by uncommenting the #define SYM line above
// also, hard coded to have no more than 20 nodes - defined with NMAX above
// path to input file is hard coded as "eg2.txt"

int main(void) {
  int i, j, n, weight, ret;

// open input file:
  FILE *fp = fopen("eg2.txt", "r");
  if(fp == NULL) {
    printf("cannot read input file\n");
    exit(1);
  }
// read number of nodes in the graph:
  fscanf(fp, "%d", &n);
  printf("n is %d\n", n);

// generate matrix:
  int **matrix;
// allocate memory:
  matrix = make2D(n, n);
// fill all elements with INF:
  for(i=0; i<n;i++)
    for(j=0; j<n; j++)
      matrix[i][j] = INF;

// read the input file:
  while( (ret = fscanf(fp, "%d %d %d", &i, &j, &weight)) != EOF) {
    if(ret == 3) {
      matrix[i][j] = weight;
#ifdef SYM
// if undirected edges, put in both paths:
      matrix[j][i] = weight;
#endif
    }
  else printf("error reading input\n");
  }
  fclose(fp);

  printMatrix(matrix, n);
  shortestPath(matrix, n);
  printMatrix(matrix, n);

}
void printMatrix(int **m, int n) {
  int i, j;
  for(i=0; i<n; i++) {
    for(j=0; j<n; j++) {
      if(m[i][j]==INF) printf("  - "); else printf("%3d ", m[i][j]);
    }
    printf("\n");
  }

}

void shortestPath(int **d, int n) {
  int i, j, k, temp;
  // no need to make a copy of the matrix: operate on the original
  for(k=0; k<n; k++) {
    for(i=0; i<n-1; i++) {
      for(j=0; j<n; j++) {
        if(i==j) continue; // don't look for a path to yourself...
        if(d[i][k] == INF || d[k][j]==INF) continue; // no path if either edge does not exist
        if((temp = d[i][k] + d[k][j]) < d[i][j]) {
          d[i][j] = temp;
#ifdef SYM
          d[j][i] = temp;
#endif
          printf("from %d to %d is shorter via %d: %d + %d is %d\n", i, j, k, d[i][k], d[k][j], temp);
        }
      }
    }
  }
  for(i=0; i<n; i++) {
    for(j=0; j<n; j++) {
      if(d[i][j] < INF) printf("%2d %2d %3d\n", i, j, d[i][j]);
    }
  }
}