我的 Prim 算法 (MST) 在矩阵中比在列表中运行得快得多

Posted

技术标签:

【中文标题】我的 Prim 算法 (MST) 在矩阵中比在列表中运行得快得多【英文标题】:My Prim's Algorithm (MST) runs much faster in a matrix than in a list 【发布时间】:2014-04-14 12:52:00 【问题描述】:

我在实现 Prim 的算法以在图中查找最小生成树时遇到了一些麻烦。 1.我生成一个邻接矩阵,并运行算法来处理这个矩阵。代码:

void Graph::Prim()
    int i=,j;
    int x=0,y=0,min,count=0;
    bool* visited=new bool[this->vertices_number]; // array of visited vertices

    for (int a=0;a<this->vertices_number;a++)
        visited[a]=false;

    visited[0]=true; // checking the first vertex (start looking here)

    while(count < this->vertices_number-1)
    min=INFINITY; //defined in header (large int)
        for (int i=0;i<this->vertices_number;i++)              
            if (visited[i]==true)                              // looking for minimum-weight edge in all visited vertices
                for(j=0;j<this->vertices_number;j++)           //looking in one column
                    if (this->AdjacencyMatrix[i][j]<min && this->AdjacencyMatrix[i][j]>0 && visited[j]==false) 
                    /* looking for lowest-weight edge which isn't already connected to the tree and isn't 0*/
                        min=AdjacencyMatrix[i][j]; //value
                        x=i;                        //row
                        y=j;                        //column
                    
                
            
        
        visited[y]=true; //  added vertex
    //  cout << x << "-->" << y << " : (" << min << ")"<<endl;
        count++; // visited vertex
    

然后,我想做同样的事情,但使用包含链接的列表数组。

class Link

public:
int destination;
int weight;
...
;  

所以我创建了一个邻接列表:

list<Link> *AdjacencyList;
AdjacencyList=new list<Link>[vertices_number];

所以最后整个列表都被填满了,例如。如果顶点 0 和 4 相连并且连接的权重为 7,则 AdjacencyList[0] 包含目的地 = 4 和权重 = 7 的链接。

我只修改了算法的主循环,所以现在它使用迭代器来查找它需要的内容:

    if (visited[i]==true) 
        for(list<Link>::iterator it = AdjacencyList[i].begin(); it != AdjacencyList[i].end(); it++)
            if ( visited[it->destination]==false && it->weight < min)
                x=i;
                y=it->destination;
                min=it->weight;

算法能够毫无问题地找到相同的 MST,唯一的是,在矩阵上工作的算法要快得多,这让我很惊讶。例如,在 80000 条边图(随机)中,使用矩阵时,树的找到时间为毫秒,但使用 list: 整秒。谁能帮我解决这个问题,或者提示如何使这段代码更有效?

【问题讨论】:

你用vector试过了吗? 尝试为列表预先分配内存?向量具有保留功能。可能列表有类似的东西吗? @Prabhu:list 没有reserve(这没有意义),但是如果您愿意使用虚拟元素,可以使用resize 我尝试了矢量,它没有改变任何东西。 【参考方案1】:

我坚信使用数组的实现比使用列表的实现更快,因为它提供了更好的内存局部性,即它对缓存更友好。

【讨论】:

确实很有可能,因为list 是一个非常专业的容器,它以内存效率换取稳定性,而vectordeque 的内存效率要高得多(以牺牲稳定性为代价)案例数)。 有趣,我认为在使用矩阵时,有时我必须遍历每个索引,这将导致 n^2 复杂度。 相关基准,如果您想用数据更新您的答案:std::vector VS std::list VS std::deque 我尝试了vector而不是list,但是仍然需要很长时间才能完成算法。如何使该程序以 O(E logV) 复杂度运行?使用矩阵的复杂度确实是O(n^2),我刚查了一下,使用列表的复杂度也是n^2,但是时间增长得更快。据此:comp.dit.ie/rlawlor/alg_ds/mst/prim.pdf 我应该得到 O(E logV)。【参考方案2】:

这是因为您正在进行非常密集的内存访问,因此密集表示会胜出。也就是说,您正在循环遍历整个邻接矩阵,按行主要顺序,这意味着您正在充分利用缓存。在链表的情况下,每个添加的新元素都会在可能不连续的堆上分配新的内存。

对于 Prim,您应该使用某种形式的优先级队列。

【讨论】:

好的,所以我应该在矩阵和列表表示中都有一个从每个顶点出来的边的优先级队列?然后我不会循环遍历数据容器,寻找最小(尚未链接)的边缘,但我会将它保存在那个队列中? 是的,优先级队列的全部目的是通过某种优先级概念来跟踪项目。在这种情况下,您希望跟踪可以添加的潜在边缘,并按边缘权重加权。这意味着您不必每次都遍历要添加的所有暂定边。

以上是关于我的 Prim 算法 (MST) 在矩阵中比在列表中运行得快得多的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Haskell 中编写 MST 算法(Prim 或 Kruskal)?

找到 MST 的关键边:可以使用修改后的 Prim 算法吗?

贪心算法之prim算法的证明

Prim 算法和 Dijkstra 算法的区别?

MST-prim

使用 Prim 和堆计算 MST 的总重量