如何从符合O(1)中某个条件的数组中选择一个随机元素

时间:2012-01-28 21:10:16

标签: c# algorithm data-structures

我有一个从数据库中获得的食谱列表,如下所示:

List<RecipeNode> _recipeList;

RecipeNode除其他外,还有一个引用一个或多个标签的属性(例如晚餐早餐 Side 素食假日,以及其他约60人。)

   public sealed class RecipeNode
   {
      public Guid RecipeId;
      public Byte[] Tags; //Tags such as 1, 5, 6, 8, 43
      //... More stuff
   }

在O(1)中从_recipeList中查找随机配方当然很容易,但我需要做的是找到一个随机配方,例如在O中的Tags中有5个(1)。

现在,我唯一的想法是制作一个List<RecipeNodes>数组,以标记键入。例如:

List<RecipeNode>[] _recipeListByTag;

然后,_recipeListByTag[5]将包含Tags数组中包含5的所有配方的列表。然后,我可以在O(1)中的该标签内选择随机允许的标签和随机配方。

这种方法的缺点是这个多维数组的大小是Recipes * Tags(例如,所有食谱中的Tags.length之和),因为我正在存储,它开始占用大量内存这个数组中可能有大量的食谱。当然,由于RecipeNode是一个引用类型,我只重复指向配方的4byte指针,所以这仍然是最好的方法。

我是否可以使用更高效的数据结构或算法来查找包含特定允许标记的随机配方?谢谢!

5 个答案:

答案 0 :(得分:8)

List<RecipeNode>[] _recipeListByTag可能是最适合您的方法,其大小 Recipes * Tags,因为数组中的每个列表只包含与匹配标记一样多的食谱,而不是更多。如果每个食谱包含每个标签,它的大小将变为Recipes * Tags

如果您的数据结构占用的内存量非常大,请不要忘记在填充每个列表后调用List.TrimExcess()。

答案 1 :(得分:2)

这是家庭作业吗?我怀疑任何真实的配方程序都要求 O(1)访问标签,并且使用数据库太慢。我也怀疑任何真实世界的配方都会有 numeric 标签。了解真实域可以帮助提供更好的答案。

然而,如果它真的是关于食谱和数字标签,并且如果你只有256个标签,为什么不选择100万次随机食谱呢?找不到具有所需标签的配方的可能性很小,复杂性仍为O(1)。如果您不喜欢这种可能性,请选择随机配方10 ^ 20次。复杂性 O(1)。

更新:

由于不是你担心的O(1),而是选择随机食谱所需的时间,我建议你让你的数据库为你处理这个问题 - 无论如何都要保存所有食谱的数据库,和你要访问的同一个数据库一起显示随机食谱。

您可以通过以下方式SELECT SQL Server中的随机记录:SQL Server Random Sort。如果您正在使用其他数据库,还有其他方法:http://www.petefreitag.com/item/466.cfm。只需确保您的WHERE子句中包含Tag=17

答案 2 :(得分:1)

如果要将数据保存在内存中,那么每个标记的(4字节)指针列表都不会做得更好。如果你可以使用数据库......那么其他人已经发布了相关信息。根据“巨大”的巨大程度,你可能只需要将一些$$$添加到目标机器上。

如果你确实希望将数据保存在内存中,但希望对内存进行简单的简约操作,可以尝试按下每个标签配方组合的4个字节。例如,将所有配方保存在一个大数组中,并且(假设您不会超过一百万个)存储数组索引,每个3个字节。

为了更进一步,您可以将带有给定标签的食谱分成相同大小的“桶”(每个桶占据大阵列的连续区域),为每个“桶”存储起始索引(3-4) bytes),然后在给定标签的连续配方的索引之间存储增量值列表。使用字节数组对delta值进行编码,使得单个delta值可以根据需要使用1-4个字节中的任何值。

由于“桶”中的配方数量将限制为常数,因此使用此方法检索仍为O(1)。

(我已经在微处理器上进行了嵌入式编程,只需要256字节的RAM ...当你这样做时,你开始考虑非常有创意的方法来节省字节甚至是比特。我相信这样长度将会如此在你的申请中没有必要,但我认为这是一个有趣的想法。)

答案 3 :(得分:0)

我会从源列表导出到另一个,并引用适合您的所有元素。根据参考文献,随机选择并从源列表中获取元素。 如果您有可能再次使用相同的派生列表,请将此类列表放入更大的列表中。 (对于cource,所选算法取决于列表的实际统计数据。)

如果您只使用一个参数,则可以通过此参数对列表进行排序,并记住在另一个列表B中,它引用具有相同参数值的元素的起始位置。之后你可以简单地在区间内随机取(B [4]; b [5] -1)。这将使随机选择的速度等于O(常数)。

答案 4 :(得分:0)

在这种情况下,我个人会选择SQlite解决方案(因为我个人认为它比其他人更好)。我发现你担心空间,而不是速度方面的性能,但就不断的恢复时间而言,你也担心数据访问的灵活性。 Imo,在这种情况下,SQlite是要走的路。

以您喜欢的方式设计您的小型数据库,并以您想要的方式执行查询和连接。

This已经过时但是有效的例子说明你如何使用它。 当然还有ORM解决方案(例如LINQ驱动程序),但对我个人而言似乎有点开销。

希望这有帮助。