使用两个对象作为unordered_map或替代方案的哈希键

时间:2015-08-28 20:27:19

标签: c++ boost hash

定义了我的对象myType后,我需要存储这些对象之间的关系。这些关系存储在矩阵中。

预先知道元素的数量,并非所有元素都有关系(element1可以与element3有关系,但可能没有与5有关)并且内存是个问题。例如,它可能看起来像:

element45与:

连接
  • 具有特征[3,1; 1,4]
  • 的元素3
  • element12,具有特征[1,1; 1,1]
  • 元素1780具有特征[8,1; 1,4]

element1661与:

连接
  • element3具有特征[3,1; 6,4]
  • element1 with characteristic [1,1; 1,9]
  • element1780,具有特征[8,1; 1,1]

有:

myType* element1;
myType* element2;

我想要(恰当地指出元素):

my_table[element1][element2][1][2]=7;

我曾考虑使用boost库创建嵌套哈希表:

boost::unordered_map<myType*, boost::unordered_map<myType*,
                std::vector<std::vector <unsigned short int> > > > my_table;

但是,即使代码编译,它也会崩溃(Segmentation fault,它指向计算哈希键的一行),运行一条简单的行,如:

my_table[element1][element2].resize(2);
for(int t=0; t<2; ++t)
    my_table[element1][element2][t].resize(2);

任何人都能对此有所了解吗?这实际上或概念上是错误的吗?

欢迎任何其他解决此问题的方法。

谢谢

2 个答案:

答案 0 :(得分:6)

对我而言,对我来说很明显,你的数据结构代表了一个图形(带有属性顶点和连接它们的边缘)。

  

此外,当你说“这些关系存储在一个矩阵。”时,你显然意味着“我把它想象成一个矩阵”,因为真正的矩阵表示¹对于更大的数字来说会变得非常低效顶点和稀疏边缘覆盖。

Boost有一个库: Boost Graph Library (BGL)

如果我们假设您希望能够读取像²

这样的图表
graph X {
    element1; element12; element166; element1780; element3; element4; 

    element45   -- element3    [ label="[3,1;1,4]" ];
    element45   -- element12   [ label="[1,1;1,1]" ];
    element45   -- element1780 [ label="[8,1;1,4]" ];

    element1661 -- element1    [ label="[1,1;1,9]" ];
    element1661 -- element3    [ label="[3,1;6,4]" ];
    element1661 -- element1780 [ label="[8,1;1,1]" ];
}

进入BGL兼容模型,使用typedef,例如:。

struct Vertex { 
    std::string node_id;
};
struct Edge {
    Box box; 
};

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;

现在您利用了BGL的全部功能:

从文件中读取图表

从graphviz文件中读取是一个功能³:

std::ifstream ifs("input.txt");
Graph result;

boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, result));
dp.property("label",   boost::get(&Edge::box, result));

read_graphviz(ifs, result, dp);

操纵图表

许多算法(dijkstra,流量,生成树,连接组件等)随时供您使用。或者你可以混合搭配。例如,让我们过滤掉没有连接的节点:

struct Filter {
    Graph const* _g;

    bool operator()(Graph::vertex_descriptor v) const {
        return boost::size(boost::adjacent_vertices(v, *_g))>0;
    }

    template <typename T>
    bool operator()(T&&) const { return true; /*catch-all*/ }
};

using Filtered = filtered_graph<Graph, Filter, Filter>;
Filter filter { &graph };
Filtered filtered(graph, filter, filter);

让我们再次将它写入graphviz:

boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, filtered));
dp.property("label",   boost::get(&Edge::box, filtered));

write_graphviz_dp(std::cout, filtered, dp);

DEMO TIME

完整演示采用输入图表:

enter image description here

并将其过滤为:

enter image description here

完整代码

<强> Live On Coliru

// http://stackoverflow.com/questions/32279268/using-two-objects-as-hash-key-for-an-unordered-map-or-alternatives
#include <cassert>
#include <iostream>

template <typename T> struct BasicBox {
    struct Point { T x, y; };

    Point tl, br;

    friend std::ostream& operator<<(std::ostream& os, Point const& p) { return os << p.x << ',' << p.y;                 } 
    friend std::ostream& operator<<(std::ostream& os, BasicBox const& b) { return os << '[' << b.tl << ';' << b.br << ']'; } 

    friend std::istream& operator>>(std::istream& is, Point& p) {
        char comma;

        if (!(is >> p.x >> comma >> p.y) && (comma == ',')) {
            is.setstate(std::ios::failbit | is.rdstate());
        }
        return is;
    } 
    friend std::istream& operator>>(std::istream& is, BasicBox& b) {
        char lbrace, semi, rbrace;
        if (!(
            (is >> lbrace >> b.tl >> semi >> b.br >> rbrace) &&
            (lbrace == '[' && semi == ';' && rbrace == ']')
        )) {
            is.setstate(std::ios::failbit | is.rdstate());
        }
        return is;
    }
};
using Box = BasicBox<int>;

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <libs/graph/src/read_graphviz_new.cpp>

struct Vertex { 
    std::string node_id;
}; 
struct Edge {
    Box box; 
};

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;

#include <fstream>
#include <boost/graph/filtered_graph.hpp>

struct Filter {
    Graph const* _g;

    bool operator()(Graph::vertex_descriptor v) const {
        return boost::size(boost::adjacent_vertices(v, *_g))>0;
    }

    template <typename T>
    bool operator()(T&&) const { return true; /*catch-all*/ }
};

int main() {
    using namespace boost;

    Graph const graph = []{ 
        std::ifstream ifs("input.txt");
        Graph result;

        boost::dynamic_properties dp;
        dp.property("node_id", boost::get(&Vertex::node_id, result));
        dp.property("label",   boost::get(&Edge::box, result));

        read_graphviz(ifs, result, dp);
        return result;
    }();

    // let's do some random task. Like. You know. Like... Filter out the unconnected nodes
    using Filtered = filtered_graph<Graph, Filter, Filter>;
    Filter filter { &graph };
    Filtered filtered(graph, filter, filter);

    boost::dynamic_properties dp;
    dp.property("node_id", boost::get(&Vertex::node_id, filtered));
    dp.property("label",   boost::get(&Edge::box, filtered));

    write_graphviz_dp(std::cout, filtered, dp);
}

¹例如BGL's AdjacencyMatrix

²选择的格式为Graphviz'DOT格式:http://www.graphviz.org/

³当然你也可以在BGL模型中使用Boost Serialization,所以你可以选择更紧凑的二进制表示,例如。

答案 1 :(得分:2)

您可以将boost::unordered_map与关键std::pair<myType*, myType*>结合使用boost::hash。您可以将其声明为:

boost::unordered_map<std::pair<myType*, myType*>, 
                     std::vector<std::vector<char>>, 
                     boost::hash<std::pair<myType*, myType*>>> dictionary;

然后你可以在字典中加载每对的特征,如下例所示:

dictionary[std::make_pair(&a, &b)] = std::vector<std::vector<char>>(1, {1, 2, 3, 4, 5});

并将其视为:

dictionary[std::make_pair(&a, &b)][0][0];

LIVE DEMO