【问题标题】:How can I hash a std::unordered_map::const_iterator?如何散列 std::unordered_map::const_iterator?
【发布时间】:2017-03-04 12:55:36
【问题描述】:

你还记得我之前的问题吗:What is causing data race in std::async here?
即使我成功地并行化了这个程序,它仍然运行太慢,不实用。
所以我尝试改进表示康威生命游戏模式的数据结构。
新结构的简要说明:

class pattern {
    // NDos::Lifecell represents a cell by x and y coordinates.
    // NDos::Lifecell is equality comparable, and has std::hash specialization.
private:
    std::unordered_map<NDos::Lifecell, std::pair<int, bool>> cells_coor;
    std::unordered_set<decltype(cells_coor)::const_iterator> cells_neigh[9];
    std::unordered_set<decltype(cells_coor)::const_iterator> cells_onoff[2];
public:
    void insert(int x, int y) {
        // if coordinate (x,y) isn't already ON,
        // turns it ON and increases the neighbor's neighbor count by 1.
    }
    void erase(int x, int y) {
        // if coordinate (x,y) isn't already OFF,
        // turns it OFF and decreases the neighbor's neighbor count by 1.
    }
    pattern generate(NDos::Liferule rule) {
        // this advances the generation by 1, according to the rule.
        // (For example here, B3/S23)
        pattern result;
        // inserts every ON cell with 3 neighbors to result.
        // inserts every OFF cell with 2 or 3 neighbors to result.
        return result;
    }
    // etc...
};

简而言之,pattern 包含单元格。它包含每个 ON 单元,以及每个具有 1 个或多个 ON 相邻单元的 OFF 单元。它还可以包含备用的OFF单元格。
cells_coor直接存储单元格,以它们的坐标为键,并将它们映射到它们的ON相邻单元格的数量(存储为int)以及它们是否为ON(存储如bool)。

cells_neighcells_onoff 通过迭代器将单元格作为键间接存储。
一个单元格的 ON 邻居数始终为 0 或更大且 8 或更少,因此cells_neigh 是一个大小为 9 的数组。
cells_neigh[0] 存储具有 0 个 ON 邻居单元的单元,cells_neigh[1] 存储具有1 ON 相邻小区,以此类推。
同样,单元格始终处于关闭或开启状态,因此 cells_onoff 是大小为 2 的数组。
cells_onoff[false] 存储关闭单元格,cells_onoff[true] 存储打开单元格。
必须在cells_coorcells_neighcells_onoff 中插入或删除单元格。换句话说,如果一个单元格被插入其中一个或从其中一个中删除,那么其他的也必须如此。因此,cells_neighcells_onoff 的元素是 std::unordered_set 将迭代器存储到实际单元格,从而可以通过邻居计数或关闭/开启状态快速访问单元格。

如果这种结构有效,插入函数的平均时间复杂度为O(1),擦除也为O(1),生成O(cells_coor.size()),与之前的结构相比,时间复杂度大大提高。

但正如您所见,有一个问题:我怎样才能散列std::unordered_map::const_iterator
std::hash 禁止对它们进行专门化,所以我必须自定义一个。
获取他们的地址是行不通的,因为它们通常是作为右值或临时值获得的。
取消引用它们也不起作用,因为有多个单元格的相邻单元格为 0,或者多个单元格处于关闭状态等。
那么我能做什么呢?如果我什么都做不了,cells_neighcells_onoff 将是 std::vector 或其他什么,会大大降低时间复杂度。

【问题讨论】:

  • 您认为std::hash 禁止他们专业化是有原因的吗?我想我可以明白你为什么想要这个,但 IMO 这是另一个证据,证明存储迭代器并不总是最好的主意。不过,我没有你的替代品。顺便说一句,您的地图缺少值类型;你的意思是unordered_set
  • @LightnessRacesinOrbit 我不知道为什么std::hash 会这样做,但是是的,我想存储迭代器,因为它们中必须有通信。如果一个单元格插入到一个单元格,它也必须插入到其他单元格,等等。
  • @Tony D 对不起,他们一定是std::unordered_set。固定。
  • 您可以存储std::unordered_map::value_type*,而不是将迭代器存储为键,就像使用&amp;*iter 获得的那样。或者至少,您可以通过散列&amp;*iter 指针来实现迭代器的散列。
  • @DannyuNDos:“时间复杂度的一点点改进就是伟大的” 不,只有当你知道你需要它时。如果你只有几个元素,真的没有理由去哈希图,尤其是当你..你知道..不能。在包含四个或五个元素的树中查找甚至可能比散列计算更快! (可能不是,但您明白了)理解算法复杂性很重要,但您还必须考虑程序中的常数因素。衡量实际表现。

标签: c++ c++11 hash unordered-map const-iterator


【解决方案1】:

简短的故事:这行不通(真的很好)(*1)。您可能要在地图cells_coor 上执行的大多数操作都会使其元素的任何迭代器 (but not pointers, as I learned) 无效。

如果你想在某个集合上保留我所谓的不同“视图”,那么存储实际数据的底层容器要么不被修改,要么不能使其迭代器无效(例如链表)。

也许我遗漏了一些东西,但为什么不保留 9 组单元格用于邻居计数和 2 组单元格用于开/关? (*2) 换一种说法:你真正需要这张地图是为了什么? (*3)


(*1):地图仅在重新散列发生时使指针和迭代器无效。你可以检查一下:

// Before inserting
(map.max_load_factor() * map.bucket_count()) > (map.size() + 1)

(*2):9 组可以减少到 8 组:如果一个单元格 (x, y) 不在 8 组中,那么它将在第 9 组中。因此存储该信息是不必要的。开/关也一样:存储打开的单元格就足够了。所有其他都关闭。

(*3):在不使用地图的情况下访问邻居的数量,而只使用一组单元格,一种伪代码:

unsigned number_of_neighbours(Cell const & cell) {
  for (unsigned neighbours = 9; neighbours > 0; --neighbours) {
    if (set_of_cells_with_neighbours(neighbours).count() == 1) {
      return neighbours;
    }
  }
  return 0;
}

在集合中重复查找当然会破坏实际性能,您需要对此进行分析。 (渐近运行时不受影响)

【讨论】:

  • 天哪。我错过了……好吧,为了解释,我需要通过坐标、邻居计数或开/关状态快速访问单元格。 cells_coorcells_neighcells_onoff分别对应它们。
  • 没错,但是您从地图中获得了哪些您无法从集合中获得的信息?邻居的数量?查看 9 组中的每组。开关?如果单元格不在现场,那么它就处于关闭状态,不是吗?顺便说一句,你只能在片场保留一个,为邻居保留 8 个。
  • 邻居计数和开/关状态属于坐标...如果我单独存储坐标、邻居计数和开/关状态,我将无法知道什么属于什么。
  • 也许我应该尝试存储 `std::reference_wrapper
  • @T.C.的确,我并不知道。更正了那部分,尽管我必须考虑原始方法是否会比我的建议更好。
猜你喜欢
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 2020-06-15
  • 2012-11-23
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多