计算图形直径的算法的正确性

时间:2017-06-02 15:51:07

标签: algorithm graph-theory graph-algorithm breadth-first-search

我是一名入门CS课程的助教,给学生的一个问题是如何使用BFS来确定未加权的无向图的直径。学生被告知他们不会因效率而被评分,所以预期的答案是一个强力算法,他们从每个节点到每个其他节点运行BFS并返回这些BFS运行的最大距离。向学生提供了他们可以在伪代码中引用的BFS方法,该伪代码将节点作为输入并返回两个映射:一个从图中的每个节点到它与起始节点的距离(称为distmap),每个节点一个从输入节点(称为父图)的最短路径到其“父节点”。

一名学生写了以下算法:

  
      
  1. 从图表中选择一个任意节点并从中运行BFS。
  2.   
  3. 在父图中创建一个非值的节点的集合Temp(即BFS树的叶子)
  4.   
  5. 将max_dist初始化为0
  6.   
  7. 对于Temp中的每个节点n:      
        
    1. 从n
    2. 运行BFS   
    3. 对于distmap中的每个值d:      
          
      1. IF d> max_dist然后将max_dist设置为等于d
      2.   
    4.   
  8.   
  9. RETURN max_dist
  10.   

我相信这个答案是正确的,但我无法证明这一点。有人可以证明它为什么起作用或提供一个反例吗?

2 个答案:

答案 0 :(得分:2)

假设不在父图中意味着在BFS树中成为叶子,则该算法是错误的。

让图形有10个顶点和下面的无向边:

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

其中一个有效的BFS树(根0)是:

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

叶子为6, 7, 8, 9,因此此解决方案返回3。 那是错的。答案是4(这是35之间的距离。)

采取所有最远的节点对于此测试都不起作用。

您可以通过生成数百万个小型随机测试用例并检查解决方案是否产生正确答案,而不是要求某人找到反例。这是我用来生成这种情况的代码(它看起来不太好,但它可以完成这项工作):

pair<vector<int>, set<int>> bfs(int st, const vector<vector<int>>& g) {
    int n = g.size();
    vector<int> d(n, n);
    d[st] = 0;
    queue<int> q;
    q.push(st);
    set<int> parents;
    while (!q.empty()) {
        int v = q.front();
        q.pop();
        for (int to : g[v])
            if (d[to] > d[v] + 1) {
                d[to] = d[v] + 1;
                q.push(to);
                parents.insert(v);
            }
    }
    return {d, parents};
}

int get_max_dist(const vector<vector<int>>& g) {
    int res = 0;
    for (int i = 0; i < (int)g.size(); i++) {
        auto cur = bfs(i, g).first;
        for (int x : cur)
            cerr << x << " ";
        cerr << endl;
        res = max(res, *max_element(cur.begin(), cur.end()));
    }
    cerr << endl;
    return res;
}

int get_max_dist_weird(const vector<vector<int>>& g) {
    auto p = bfs(0, g);
    vector<int> cand;
    int n = g.size();
    for (int i = 0; i < n; i++)
        if (!p.second.count(i))
            cand.push_back(i);
    int res = 0;
    for (int i : cand) {
        auto cur = bfs(i, g).first;
        res = max(res, *max_element(cur.begin(), cur.end()));
    }
    return res;
}

vector<vector<int>> rand_graph(int n) {
    vector<vector<int>> g(n);
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            if (rand() & 1) {
                g[i].push_back(j);
                g[j].push_back(i);
            }
    return g;
}

int main() {
    for (int i = 1;; i++) {
        int n = 10;
        auto g = rand_graph(n);
        int correct = get_max_dist(g);
        int got = get_max_dist_weird(g);
        if (correct != got) {
            cerr << correct << " " << got << endl;
            for (int v = 0; v < n; v++)
                for (int j : g[v])
                    if (v < j)
                        cerr << v << " " << j << endl;
        }
        assert (get_max_dist_weird(g) == get_max_dist(g));
        if (i % 1000 == 0)
            cerr << i << endl;
    }
}

当然,你不能用这种方式证明算法是正确的,但如果不是这样的话,它很可能找到一个反例。

答案 1 :(得分:2)

可能是一个稍微简单的反例:

enter image description here

很明显,此图中的最大距离在绿色节点(4)之间,但如果从红色节点启动BFS,Temp将仅由两个蓝色节点组成,这给出了不正确的&#34;直径&#34; 3。