快速从C#散列集中获取随机元素
我需要存储一组元素。我需要的是功能快速从C#散列集中获取随机元素,c#,performance,random,hashset,C#,Performance,Random,Hashset,我需要存储一组元素。我需要的是功能 移除(单个)元件和 添加(组)元素和 每个对象只能在集合中存在一次,并且 从集合中获取一个随机元素 我选择了HashSet(C#),因为它采用了fast方法来删除元素(HashSet.remove(element)),添加集合(HashSet.UnionWith(anotherHashSet)),并且HashSet的性质保证没有重复项,所以需要考虑1到3 我发现获得随机元素的唯一方法是 Object object = hashSet.ElementAt(rnd
Object object = hashSet.ElementAt(rnd.Next(hashSet.Count));
但这是非常缓慢的,因为我对地图的每个像素都调用一次(从多个起点创建一个随机的泛洪填充;mapsize 500x500,但我现在想变大),并且哈希集包含相当多的项。(快速测试显示,在再次缩小之前,它最多会放大5752个条目。)
分析(CPU采样)告诉我ElementAt调用占50%以上
我意识到在一个大的hashset上执行500x500操作并不容易,但其他操作(Remove和UnionWith)的调用频率与ElementAt相同,因此主要问题似乎是操作,而不是调用的数量
我模模糊糊地理解为什么从哈希集中获取某个元素非常昂贵(与从列表或其他有序数据结构中获取元素相比,我只想随机选取。这真的很难,而且没有办法解决吗?有更好的数据结构适合我吗
将所有内容都更改为列表并没有帮助,因为现在其他方法会成为瓶颈,而且需要更长的时间
将HashSet强制转换为数组并从中拾取我的随机元素并没有什么帮助,因为虽然从数组中拾取随机元素很快,但将HashSet强制转换为数组首先需要比单独运行HashSet.ElementAt更长的时间
如果您想更好地理解我要做的事情:基本问题是索引 在数组或列表中,数据通过其坐标索引(通常只是一个简单的int索引)进行索引。在
哈希集中,您可以自己选择索引(即键)。不过,副作用是,没有“坐标”(即“索引3处的元素”问题)真的没有意义。它的实际实现方式是一项一项地枚举整个哈希集,然后返回第n项。这意味着要获得第1000项,还必须枚举之前的所有999项。这很痛苦
解决此问题的最佳方法是根据散列集
的实际键选择随机键。当然,只有在这样选择随机键是合理的情况下,这才有效
如果您无法以令人满意的方式随机选取密钥,您可能需要保留两个单独的列表-每当您将新项添加到哈希集
,将其密钥添加到列表
;然后您可以轻松地从列表
中选取一个随机密钥,并按照它进行操作。根据您的要求,重复项可能不太重要问题
当然,如果只执行一次枚举,则可以在元素上保存枚举。例如,在搜索哈希集之前,您可以将其转换为列表
。当然,这只有在同时拾取多个随机索引时才有意义(例如,如果您一次随机选择5个索引,平均可以节省约五分之一的时间)-如果您总是选择一个,那么修改哈希集并选择另一个,这将毫无帮助
根据您的具体用例,可能还值得一看SortedSet
。它的工作方式类似于HashSet
,但它保持键的顺序。有用的部分是,您可以使用GetViewBetween
方法来获取整个键范围-如果您的键是稀疏,但在任意范围之间很好地平衡。您只需首先随机选择一个范围,然后使用GetViewBetween
获取范围内的项目,并从中随机选择一个。实际上,这将允许您对搜索结果进行分区,并应可节省大量时间。我认为这可能适合您的目的:
var dict = new OrderedDictionary();
dict.Add("My String Key", "My String");
dict.Add(12345, 54321);
Console.WriteLine(dict[0]); // Prints "My String"
Console.WriteLine(dict[1]); // Prints 54321
Console.WriteLine(dict["My String Key"]); // Prints "My String"
Console.WriteLine(dict[(object)12345]); // Prints 54321 (note the need to cast!)
这具有快速添加和删除,以及O(1)索引。它只适用于对象
键和值,但没有通用版本。您要删除什么?它只是随机找到的元素,还是任意的?为什么不使用哈希集进行所有添加和删除操作,然后在您要进行随机像素获取之前,只需转换到列表一次?使用该列表,然后hr然后离开。除非你需要同时添加、删除和获取随机元素…@spender我删除随机找到的元素only@Baldrick我担心是后者。循环基本上是:选择一个随机单元(散列集包含随机泛洪填充可以扩散到的所有可能单元,即“边缘”)->填充它->查找相邻的空单元格并将它们添加到哈希集中->从哈希集中删除填充的单元格->再次循环,直到哈希集为空。我觉得二维链表将是您的朋友。是的,我想用一个列表和一个哈希集对其进行索引。@spender是的,如果您不关心删除垃圾,这可以很好地工作。但是,如果这样做,它可能会变得非常昂贵。我要从中随机选取一个对象的对象是网格中的单元格,因此应该很容易给它们一个唯一的ID(x坐标到字符串+y坐标到字符串?),所以如果我想重写Cell类中的GetHashCode吗“根据哈希集的实际键随机选取”?@Christiangese你现在使用的键是什么?整个单元格?那是什么类型的?是的,整个单元格。不确定“类型”是什么意思。它只是一个自定义类,包含一些信息,没有继承