【问题标题】:Which is best data structure for searching and counting of pairs of objects in vector of vectors哪个是用于在向量向量中搜索和计数对象对的最佳数据结构
【发布时间】:2017-02-03 12:32:30
【问题描述】:

我已经定义了 class elementclass node

类元素

class element
{
    int id;
    std::vector<node> m_nodes;   // An element consist of 4 nodes.
    public:
    getnode(int)    // return n-th node;       
}

类节点

class node
{
    int id;
    // other members
}

class model 由整个 nodeelement 对象组成。 class element 对象由四个 node 对象组成的向量。一对两个连续(相邻的)node 对象称为面。

示例:
elem1 : { 1,2,3,4 }
elem2 : { 3,5,6,4 }
elem1 和 elem2 是两个element 对象,数组中的整数表示四个节点对象的id。
1-2、2-3、3-4 和 4-1 是 elem1 的面。和 3-5、5-6、6-4 和 4-3 是 elem2 的面。 面 3-4 和面 4-3 是相同的,因此由两个元素共享

边界元素是由至少一个面组成的元素,不被其他元素共享。在上面的例子中,elem1 和 elem2 都是边界元素。模型类中还定义了边界元素的向量。

类模型

class model    
{    
    std::vector<node> m_nodes;
    std::vector<element>m_elements;
    std::vector<element>m_boundary;

    public:
    void set_boundary_elements();
}

问题:如何初始化边界元素的向量
这是 set_boundary_elements() 函数的伪代码。

void model::set_boundary_elements()
{
     std::vector <std::pair<std::set<int> , int >> faces;
     std::set<int> s;
     for(auto iter::m_elements)
     {
         //initialise  face.
         for(int i=1; i<5; ++i)
         {
             if(i != 4)
              {
                  s.insert(iter.getnode(i));
                  s.insert(iter.getnode(i+1));
              }
              else 
              {
                  s.insert(iter.getnode(4));s.insert(iter.getnode(1));   
              }
              for(auto it: faces)
              {
                  if(s== it.first)
                       (it.second)++; break;
              }
              faces.push_back(s,1);
         }
         //then push_back the elements which have nonshared faces, into m_boundary.
     }
}             

我认为我的算法效率低下,因为每次我必须遍历所有面孔时都添加一个面孔。 stl/algorithm中是否有任何有用的方法可以有效地解决我的问题?

【问题讨论】:

  • 顺便说一句,如果您保证 element 只拥有 4 个 node 实例,那么 std::array 可能是比 std::vector 更好的选择。
  • 所以听起来你有一个图表,概念上。您可能需要根据某些计算的重要性重新考虑您的模型。元素实际上是可以用邻接表表示的子图。找到边界元素将包括从某个集合 i 中减去所有其他集合的并集。 .我不知道这是否对您有任何帮助,但也许从集合和图论重新思考设计可能会对您有所帮助。
  • 如前所述,对静态大小的向量使用 std::array 并在 for 循环中使用 const 引用:for (const auto &iter: m_elements)

标签: c++ algorithm stl set std-pair


【解决方案1】:

重新考虑您的设计。目前,元素并不真正共享节点。应该共享一个节点的两个元素只是各自存储一组不同的数据,这些数据恰好具有相同的 id。这意味着如果某个节点发生变化,系统可能会不一致。

这是我的建议(不包括构造函数、getter、setter 等,假设您可以轻松填写​​这些内容):

class Model {

    class Node;
    class Element;

private:

    vector<Node> nodes;
    vector<Element> elements;

}

class Model::Element {
private:
    Element(); //only to be created by Model
    vector<unsigned int> incident_nodes;
}

class Model::Node {
private:
    Node(); //only to be created by Model
    vector<unsigned int> incident_elements;
}

请注意,Node 和 Element 都存储事件项,并且它们使用 整数 在 Model 中的向量中引用它们的 id。模型负责创建和修改节点和元素,这些方法将负责保持数据的一致性。 Element 或 Node 上的所有公共方法都是常量。

这为您提供了一个在两个方向都有引用的稳定系统。如果你想知道一个Element是否在边界上,代码是

//returns all entries that are in both vectors
inline vector<unsigned int> intersection(const vector<unsigned int>& vector_a,
    const vector<unsigned int>& vector_b);

typedef vector<unsigned int> Face; //defined in model, a pair of node ids

//number = 0..3, returns the corresponding face
Model::Face Model::get_face(const unsigned int element_id,
    const unsigned int number);

vector<unsigned int> Model::incident_elements(const Face& face){
    return intersection(nodes[face[0]].incident_elements,
        nodes[face[1]].incident_elements);
}

bool Model::is_boundary(const unsigned int element_id){

    //check if it has a face that is boundary
    for (unsigned int i=0; i<4;i++){
        Face face = get_face(element_id, i); 
        if(incident_elements(face).size() == 1){
            return true;
        }
    }
    return false;
}

(所有引用的方法和函数都应该是不言自明的,Face 可以转换为结构或类,可能使用方法 Face::incident_elements{return intersection(...);},特别是如果你想这样做脸上有更多的东西,但 Face 对象可能是临时的,因为它们很容易被提取)

这种方式可以让你清理操作,当然每个节点都需要存储事件元素向量,这需要更多的内存。但我怀疑如果没有这样的事情你是否能够有效地工作,特别是因为我假设你会想要做更多这样的操作。

可以用静态大小的东西替换 Node 和 Element 中的向量,但我认为这没什么大不了的,尤其是因为它们只能在 Model 中访问。

该架构的缺点是删除效率低下(更改所有 id 存储)或在内存中留下漏洞(尽管如果存储了未使用的 id 列表,这还不错)

【讨论】:

    【解决方案2】:

    正如在您帖子的评论中所说,将 std::array 用于静态大小的向量并在您的 for 循环中使用 const 引用,这将避免复制并有助于优化:

     for (const auto &iter: m_elements)
    

     for(auto &it: faces)
    

    如果您有很多元素(> 50),我认为您还应该将用于面部的容器从 std::vector 更改为 std::map,这样:

              for(auto it: faces)
              {
                  if(s== it.first)
                       (it.second)++; break;
              }
              faces.push_back(s,1);
    

    会变成:

             auto &it = faces.find(s);
             if (it != faces.end())
                 it.second++;
             else
                 faces.insert(std::make_pair(s, 1));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 1970-01-01
      • 2020-05-03
      • 1970-01-01
      • 2013-03-09
      • 2013-01-12
      相关资源
      最近更新 更多