纯粹的查找速度单值类型c#?

时间:2014-05-30 16:13:47

标签: c# arrays performance linq

.NET 4.5.1

我有一群#34;"适用范围从-4到32760的Int16值。范围内的数字不是连续的,但它们从-4到32760排序。换句话说,16-302中的数字不在&#34中;束",但是号码303-400在那里,号码2102不存在,等等。

确定某个特定值(例如18400)是否在"束"中的最快速的方法是什么?现在它在Int16 []中并且Linq Contains方法用于确定数值是否在数组中,但是如果有人可以说明为什么/如何不同的结构将更快地传递单个值,我将不胜感激。速度是此查找的关键("束"是静态类的静态属性)。

有效的示例代码

Int16[] someShorts = new[] { (short)4 ,(short) 5 , (short)6};
var isInIt = someShorts.Contains( (short)4 );

我不确定这是否是可以完成的最高效的事情。

感谢。

6 个答案:

答案 0 :(得分:8)

听起来你真的想要BitArray - 只是将值偏移4,这样你就可以获得[0, 32764]的范围,你应该没问题。

这将分配一个有效4K大小(32764/8)的数组,数组中每个值一位。它将处理查找数组中的相关元素,并应用位屏蔽。 (我不知道它是否在内部使用byte[]或其他东西。)

这可能不像存储范围那样紧凑,但获取/设置位所涉及的成本将计算索引(基本上是一个移位),获取相关的内存位CPU,然后位掩码。它需要bool[]大小的1/8,从而提高CPU缓存的使用效率。

当然,如果这对您来说确实是一个性能瓶颈,那么您应该在实际应用中比较这个解决方案和bool[]方法 - 微基准测试在这里与您的真实应用程序几乎一样重要的行为。

答案 1 :(得分:3)

为每个可能的值制作一个bool:

var isPresentItems = new bool[32760-(-4)+1];

如果给定项目存在于集合中,则将相应元素设置为true。查找很简单:

var isPresent = isPresentItems[myIndex];

不能更快地完成。 bool将适合L1或L2缓存。

我建议不要使用BitArray,因为它每个字节存储多个值。这意味着每次访问都较慢。需要进行位运算。

如果你想要疯狂的速度,不要让LINQ为每个项目召唤一次代表。 LINQ不是性能关键代码的首选。许多延迟CPU的间接。

答案 2 :(得分:2)

如果要优化查找时间,请选择具有O(1)(常量)查找的数据结构。您有几个选择,因为您只关心集合成员资格,而不是排序或排序。

HashSet<Int16>将为您提供此功能,BitArray上的max - min + 1索引也是如此。正如@usr建议的那样,绝对最快的临时解决方案可能是max - min + 1上索引的简单数组。任何这些应该足够“足够快”。 HashSet<Int16>可能会使用最多的内存,因为内部哈希表的大小是一个实现细节。 <{1}}将是这些选项中空间效率最高的。

如果您只有一次查找,那么内存不应该是一个问题,我建议先使用BitArray。该解决方案很容易以无错误的方式进行推理和处理,因为您不必担心保持在数组边界内;您只需查看HashSet<Int16>即可。如果您的值范围将来可能会发生变化,这将特别有用。如果您需要进一步优化速度或性能,您可以回退到其他解决方案之一。

答案 3 :(得分:1)

一种选择是使用HashSet。要查找值是否在其中,它是O(1)操作

代码示例:

HashSet<Int16> evenNumbers = new HashSet<Int16>();

    for (Int16 i = 0; i < 20; i++)
    {
        evenNumbers.Add(i);
    }


    if (evenNumbers.Contains(0))
    {
       /////
    }

答案 4 :(得分:0)

因为数字已经排序,我会循环遍历列表一次并生成一个包含起始和结束编号的Range对象列表。该列表将比拥有数千个数字的列表或字典小得多。

答案 5 :(得分:0)

如果你的&#34;束&#34;数字可以被识别为一系列间隔,我建议你使用Interval Trees。区间树允许动态插入/删除,并且还搜索区间是否与树中的任何区间相交是O(log(n)),其中n是树中区间数。在您的情况下,间隔的数量将小于整数的数量,搜索速度会快得多。