我正在研究用于化学配方的科学研究软件。我使用内部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,因此您可以更深入地查看课程,而不是将其复制并粘贴到此处。
答案 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;
}
我猜你正在研究有机化学,所以你可能不需要处理那么多的同位素,如果查找是问题,这个会非常快......