尝试更新类型Dictionary <icomparable,int =“”> </icomparable,>中的值时,防止双重哈希操作

时间:2012-07-30 19:48:53

标签: c# data-structures chemistry

我正在研究用于化学配方的科学研究软件。我使用内部Dictionary<Isotope, int>跟踪化学式的内容,其中Isotope是像“Carbon-13”,“Nitrogen-14”这样的对象,int代表数字化学式中的那些同位素。因此,公式C2H3NO将如下存在:

{"C12", 2
"H1", 3
"N14", 1
"O16", 1}  

这一切都很好,但是当我想要将两个化学式一起添加时,我最终必须计算两次Isotope的哈希函数来更新一个值,请参阅下面的代码示例。

public class ChemicalFormula {
    internal Dictionary<Isotope, int> _isotopes = new Dictionary<Isotope, int>();

    public void Add(Isotope isotope, int count)
    {
        if (count != 0)
        {
            int curValue = 0;
            if (_isotopes.TryGetValue(isotope, out curValue))
            {
                int newValue = curValue + count;
                if (newValue == 0)
                {
                    _isotopes.Remove(isotope);
                }
                else
                {
                    _isotopes[isotope] = newValue;
                }
            }
            else
            {
                _isotopes.Add(isotope, count);
            }
            _isDirty = true;
        }
    }
}

虽然这看起来似乎不会慢下来,但是当我们将数十亿的化学式一起添加时,这种方法始终是程序中最慢的部分(> 45%的运行时间)。我正在处理大型化学公式,如“H5921C3759N1023O1201S21”,这些化学公式一直被小化学公式所加入。

我的问题是,是否有更好的数据结构来存储这样的数据?我已经尝试创建一个包含IsotopeCount的简单int对象,因此我可以访问引用类型中的值(而不是value-type)以避免双重哈希函数。然而,这似乎没有益处。

修改 Isotope是不可变的,在程序的生命周期内不应该更改,因此我应该能够缓存哈希码。

我已链接到source code,因此您可以更深入地查看课程,而不是将其复制并粘贴到此处。

4 个答案:

答案 0 :(得分:0)

  

我尝试创建一个包含int的简单IsotopeCount对象,这样我就可以访问引用类型中的值(而不是value-type)以避免双重哈希函数。然而,这似乎没有益处。

那么它会阻止双重散列...但显然它在空间方面更糟糕。您注意到 的性能差异是什么?

如果你这么做的话,你应该强烈考虑的另一个选择是在Isotope类中缓存哈希,假设它是不可变的。 (如果不是,那么使用它作为字典键至少有点担心。)

如果您可能使用大多数Isotope值作为字典键(或候选词),那么在初始化期间计算哈希值可能是值得的。否则,选择一个特别不可能的哈希值(在理想世界中,可以是任何值)并将其用作“未缓存”值,并懒惰地计算它。

如果您在GetHashCode中有45%的运行时间,那么您是否考虑过优化它?它实际上是GetHashCode还是Equals? (你说的是“哈希”,但我怀疑你的意思是“一般的哈希查找”。)

如果您可以发布Isotope类型的相关位,我们可能会提供更多帮助。

编辑:如果你使用.NET 4,考虑的另一个选择是ConcurrentDictionary及其AddOrUpdate方法。你会这样使用它:

public void Add(Isotope isotope, int count)
{
    // I prefer early exit to lots of nesting :)
    if (count == 0)
    {
        return 0;
    }

    int newCount = _isotopes.AddOrUpdate(isotope, count, 
                                         (key, oldCount) => oldCount + count);
    if (newCount == 0)
    {
        _isotopes.Remove(isotope);
    }
    _isDirty = true;
}

答案 1 :(得分:0)

您是否确实需要按类型随机访问同位素计数,或者您是否使用字典作为将键与值相关联的方法?

我会猜测后者。

我对你的建议不是使用字典,而是使用IsotopeTuples的排序数组(或List),例如:

class IsotopeTuple{
   Isotope i;
   int count;
}

按同位素的名称排序。

为什么要排序?

因为那样,当你想要将两个同位素“加”在一起时,你可以通过遍历两个数组在线性时间内做到这一点(希望这很清楚,如果需要我可以详细说明)。不需要哈希计算,只需对订单进行超快速比较。

这是处理矢量乘法时的经典方法,其中维度是单词。 广泛用于文本挖掘。

权衡当然是初始向量的构造是(n)log(n),但我怀疑你是否会感受到这种影响。

答案 2 :(得分:0)

我认为应该使用预先计算的哈希使Isotope成为不可变的。这会使一切变得更加简单。

(事实上,面向功能的编程更适合这种类型的计算,它处理不可变对象)

答案 3 :(得分:0)

如果您的同位素数量有限并且没有内存问题,您可以想到另一个解决方案:

public struct Formula
{
   public int C12;
   public int H1;
   public int N14;
   public int O16;
}

我猜你正在研究有机化学,所以你可能不需要处理那么多的同位素,如果查找是问题,这个会非常快......