【发布时间】: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_neigh 和 cells_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_coor、cells_neigh 和cells_onoff 中插入或删除单元格。换句话说,如果一个单元格被插入其中一个或从其中一个中删除,那么其他的也必须如此。因此,cells_neigh 和 cells_onoff 的元素是 std::unordered_set 将迭代器存储到实际单元格,从而可以通过邻居计数或关闭/开启状态快速访问单元格。
如果这种结构有效,插入函数的平均时间复杂度为O(1),擦除也为O(1),生成O(cells_coor.size()),与之前的结构相比,时间复杂度大大提高。
但正如您所见,有一个问题:我怎样才能散列std::unordered_map::const_iterator?std::hash 禁止对它们进行专门化,所以我必须自定义一个。
获取他们的地址是行不通的,因为它们通常是作为右值或临时值获得的。
取消引用它们也不起作用,因为有多个单元格的相邻单元格为 0,或者多个单元格处于关闭状态等。
那么我能做什么呢?如果我什么都做不了,cells_neigh 和 cells_onoff 将是 std::vector 或其他什么,会大大降低时间复杂度。
【问题讨论】:
-
您认为
std::hash禁止他们专业化是有原因的吗?我想我可以明白你为什么想要这个,但 IMO 这是另一个证据,证明存储迭代器并不总是最好的主意。不过,我没有你的替代品。顺便说一句,您的地图缺少值类型;你的意思是unordered_set? -
@LightnessRacesinOrbit 我不知道为什么
std::hash会这样做,但是是的,我想存储迭代器,因为它们中必须有通信。如果一个单元格插入到一个单元格,它也必须插入到其他单元格,等等。 -
@Tony D 对不起,他们一定是
std::unordered_set。固定。 -
您可以存储
std::unordered_map::value_type*,而不是将迭代器存储为键,就像使用&*iter获得的那样。或者至少,您可以通过散列&*iter指针来实现迭代器的散列。 -
@DannyuNDos:“时间复杂度的一点点改进就是伟大的” 不,只有当你知道你需要它时。如果你只有几个元素,真的没有理由去哈希图,尤其是当你..你知道..不能。在包含四个或五个元素的树中查找甚至可能比散列计算更快! (可能不是,但您明白了)理解算法复杂性很重要,但您还必须考虑程序中的常数因素。衡量实际表现。
标签: c++ c++11 hash unordered-map const-iterator