繁体   English   中英

不使用 GetHashCode 的 HashSet 和 Dictionary 的 C# 高性能替代方案

[英]C# performant alternatives to HashSet and Dictionary that do not use GetHashCode

我正在寻找比列表具有更好性能但不使用内部GetHashCode方法的HashSetDictionary对象的内置替代方案。 我需要这个,因为我写的类,没有写的方式GetHashCode是满足与通常的合同法Equals比其他

public override int GetHashCode() { return 0; } // or return any other constant value

这会将HashSetDictionary变成普通列表(性能方面)。

所以我需要的是一个集合实现和一个映射实现。 有什么建议吗?

编辑:

我的类是基于容差的 3 维向量类:

public class Vector
{
    private static const double TOL = 1E-10;
    private double x, y, z;

    public Vector(double x, double y, double z)
    {
        this.x = x; this.y = y; this.z = z;
    }

    public override bool Equals(object o)
    {
        Vector other = o as Vector;

        if (other == null)
            return false;

        return ((Math.Abs(x - other.x) <= TOL) &&
                (Math.Abs(y - other.y) <= TOL) &&
                (Math.Abs(z - other.z) <= TOL));
    }
}

请注意,我的Equals方法不是可传递的。 但是,在我的用例中,我可以使其“本地”传递,因为在某些时候,我将知道需要放入我的集合/映射键集中的所有向量,并且我也知道它们将成簇出现。 所以当我收集了所有向量后,我会为每个集群选择一个代表,并用代表替换所有原始向量。 然后Equals将在我的集合/映射键集合的元素之间传递。

当我有我的集合或映射时,我将从另一个来源收集向量(为了这个问题,我们假设我会要求用户输入一个向量)。 这些可以是任何可能的向量。 这些永远不会被添加到集合/映射中,但我需要知道它们是否包含在映射的集合/键集中(关于容差),我需要从映射中知道它们的值。

您需要一个支持排序、二分查找和快速插入的数据结构。 不幸的是,.NET Framework 中没有这样的集合。 SortedDictionary不支持二分查找,而SortedList对未排序的数据进行 O(n) 插入。 所以你必须搜索第三方工具。 一个不错的候选者似乎是C5库的TreeDictionary 它是一个红黑树实现,提供了重要的方法RangeFromTo 这是一个字典的不完整实现,它以 Vectors 作为键,内部由 C5.TreeDictionary 支持:

public class VectorDictionary<TValue>
{
    private readonly C5.TreeDictionary<double, (Vector, TValue)> _tree =
        new C5.TreeDictionary<double, (Vector, TValue)>();

    public bool TryGetKeyValue(Vector key, out (Vector, TValue) pair)
    {
        double xyz = key.X + key.Y + key.Z;
        // Hoping that not all vectors are crowded in the same diagonal line
        var range = _tree.RangeFromTo(xyz - Vector.TOL * 3, xyz + Vector.TOL * 3);
        var equalPairs = range.Where(e => e.Value.Item1.Equals(key));
        // Selecting a vector from many "equal" vectors is tricky.
        // Some may be more equal than others. :-) Lets return the first for now.
        var selectedPair = equalPairs.FirstOrDefault().Value;
        pair = selectedPair;
        return selectedPair.Item1 != null;
    }

    public Vector GetExisting(Vector key)
    {
        return TryGetKeyValue(key, out var pair) ? pair.Item1 : default;
    }

    public bool Contains(Vector key) => TryGetKeyValue(key, out var _);

    public bool Add(Vector key, TValue value)
    {
        if (Contains(key)) return false;
        _tree.Add(key.X + key.Y + key.Z, (key, value));
        return true;
    }

    public TValue this[Vector key]
    {
        get => TryGetKeyValue(key, out var pair) ? pair.Item2 : default;
        set => _tree.Add(key.X + key.Y + key.Z, (key, value));
    }

    public int Count => _tree.Count;
}

用法示例:

var dictionary = new VectorDictionary<int>();
Console.WriteLine($"Added: {dictionary.Add(new Vector(0.5 * 1E-10, 0, 0), 1)}");
Console.WriteLine($"Added: {dictionary.Add(new Vector(0.6 * 1E-10, 0, 0), 2)}");
Console.WriteLine($"Added: {dictionary.Add(new Vector(1.6 * 1E-10, 0, 0), 3)}");
Console.WriteLine($"dictionary.Count: {dictionary.Count}");
Console.WriteLine($"dictionary.Contains: {dictionary.Contains(new Vector(2.5 * 1E-10, 0, 0))}");
Console.WriteLine($"dictionary.GetValue: {dictionary[new Vector(2.5 * 1E-10, 0, 0)]}");

输出:

Added: True
Added: False
Added: True
dictionary.Count: 2
dictionary.Contains: True
dictionary.GetValue: 3

在您的情况下,您可以获得相当好的哈希码实现。 请记住,哈希码最重要的规则如下:

  • 两个相等的向量必须返回相同的值

这并不意味着两个不同的向量不能返回相同的值; 在某些情况下,他们显然必须这样做,散列的数量是有限的,用于所有目的的不同向量的数量不是。

好吧,考虑到这一点,只需根据截断为容差有效数字减一的向量坐标来评估您的哈希码。 所有相等的向量都会给你相同的散​​列和一小部分不相等的向量,它们的最后一个十进制不同......你可以接受。

更新:更改为四舍五入为截断。 舍入不是正确的选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM