【问题标题】:cpp unordered_set just use comparator not hashcpp unordered_set 只使用比较器而不是哈希
【发布时间】:2020-03-28 02:05:50
【问题描述】:
#include <unordered_set>
#include <iostream>

class edge{
    public:
        float a1;
        float a2;

};

struct comp{
    bool operator()(const edge& e1, const edge& e2) const {
        return true;
        return (
            (e1.a1==e2.a1 && e1.a2==e2.a2) ||
            (e1.a1==e2.a2 && e1.a2==e2.a1)
        );
    };
};
struct hash{
    size_t operator()(const edge& e1) const {
        // return std::hash<float>()(e1.a1+e1.a2);
        return std::hash<float>()(e1.a1+e1.a2*2);
    };
};


int main() {
    std::unordered_set<edge,hash,comp> s1;
    s1.insert(edge{1.1,2.2});
    s1.insert(edge{2.2,1.1});
    for( auto& it : s1 ) {
        std::cout << it.a1 << " " << it.a2 << "\n";
    }
    std::cout << "s1.size " << s1.size() << "\n";
}

我意识到如果不同的元素具有相同的哈希值,那么它们被认为是相等的,但我只是希望这个 unordered_set 使用我定义的比较器,只是忽略哈希?

如何实现?

我知道我可以使用set,但是使用set需要考虑顺序,如果a

如果有人能帮忙,不胜感激


编辑: 我的意图是让两条边称为 e1,e2,如果 (e1.a1==e2.a1&amp;&amp;e1.a2==e2.a2)or(e1.a1==e2.a2 &amp;&amp; e1.a2==e2.a1) 如我提供的那样,它们是相同的 在结构比较中。 但是当我测试时。似乎哈希函数也可以改变比较。有人说我定义哈希和比较器的方式会导致未定义的行为。 真的吗?为什么? 如果是真的,如何解决这个问题?我只想让比较器决定将哪一个满足插入 unordered_set 而不重复。而且真的不关心哈希。

顺便说一句,感谢很多人回复

【问题讨论】:

  • std::unordered_multiset符合您的要求吗?
  • 如果您使用std::tie,订购通常并不难。
  • 你的意图是拥有size() == 1?
  • “我意识到如果不同的元素具有相同的哈希值,那么它们被认为是相等的” - 不正确。无序集散列+比较器的要点是提供恒定时间散列和线性子时间(k)比较。 unordered_set 仍然是 set (很疯狂,对吧?)。元素是独一无二的。该唯一性是通过hash 在当前哈希表中找到正确的碰撞桶,然后通过compare 在碰撞桶中搜索匹配元素来确定的。如果这不是您想要的行为,那么就不要再尝试将方形钉子塞进圆孔中了;使用不同的容器。
  • 我相信比较哈希并不比比较一对floats快多少。无论如何,你的哈希看起来有点弱。所以我会考虑改用std::set

标签: c++ set


【解决方案1】:

如果你想交替处理edge.a1edge.a2,你必须实现一个散列函数,即使它们被交换也返回相同的值。我建议不要使用加法,因为addition may not be commutative for floats,但您可以按大小对它们进行排序,然后组合哈希:

struct hash {
  size_t operator()(const edge& e1) const {
    auto h1 = std::hash<float>{}(std::min(e1.a1, e1.a2));
    auto h2 = std::hash<float>{}(std::max(e1.a1, e1.a2));
    return h1 ^ (h2 << 1)
  };
};

这只对相当大的浮点数集有意义,否则散列开销可能会超过首先使用散列数据结构的好处。

旧答案供参考:

具有相同哈希值的对象被认为是相等的 unordered_set。它们只是存储在同一个存储桶中。有一个 KeyEqual template parameter 用于比较,由 默认使用Keyoperator==。所以你的主要问题是, comp 应该实现 e1 == e2 而不是 e1 &lt; e2(并且应该 可能被称为equal)。

哈希只是用来加速搜索、插入和删除 元素。

另一方面,您可能希望使用成员的哈希值 变量而不是值本身来计算 edge:

struct hash {
  size_t operator()(const edge& e1) const {
    auto h1 = std::hash<float>{}(e1.a1);
    auto h2 = std::hash<float>{}(e1.a2);
    return h1 ^ (h2 << 1)
  };
};

这样,交换后的两条边不会得到相同的哈希值 坐标。建议使用这种组合哈希的方式here (but is not a good way to combine more than two)。

【讨论】:

  • 如果我正确理解了这个问题,其目的是让comp 忽略a1a2 的顺序。然后问题是OP的哈希函数不正确。
  • @Evg 好吧,如果 OP 实现一个等于操作而不是比较 KeyEqual 来解决问题。
  • @eike 的目的是忽略 a1 和 a2 的顺序,因为对于边缘,哪个是第一个并不重要
  • @Athos 好的,这解释了很多。我完全误解了你的问题。正在编辑...
【解决方案2】:

不必必须使用edge 的成员来提供哈希。只要求相等的值具有相等的散列。 “始终有效”的哈希是

struct hash{
    size_t operator()(const edge& e1) const {
        return 0;
    };
};

但看来你最初的尝试更好

struct hash{
    size_t operator()(const edge& e1) const {
        return std::hash<float>{}(e1.a1 + e1.a2); // + is commutative
    };
};

【讨论】:

  • 这将使unordered_set 的所有好处无效。那为什么不直接使用set呢?
  • @eike 并非所有好处。 OP 仍然可以使用他们的 KeyEqual(混淆地称为comp
  • 但他的实现,看起来,不应该实现等于操作。
  • @Evg OPs hash + comp 给出未定义的行为
  • @eike,与comp 定义的“相同”。
猜你喜欢
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-09
  • 2017-07-21
  • 1970-01-01
  • 2019-05-17
  • 1970-01-01
相关资源
最近更新 更多