从字典中随机输入

Ed *_*mes 50 c# dictionary

在c#中从Dictionary中获取随机条目的最佳方法是什么?

我需要从字典中获取一些随机对象以显示在页面上,但是我不能使用以下内容,因为索引无法访问字典:

Random rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[rand.Next()];
Run Code Online (Sandbox Code Playgroud)

有什么建议?

Tim*_*ter 50

如果你使用.net 3.5,Enumerable有一个扩展方法ElementAt,它允许你这样做:

return dict.ElementAt(rand.Next(0, dict.Count)).Value;
Run Code Online (Sandbox Code Playgroud)

  • 啊哈,我没有打开这个命名空间,因为我的stylecop设置会自动删除它!很酷,这可能是最好的路线 (2认同)

Str*_*ior 42

更新以使用泛型,更快,并解释为什么此选项更快.

这个答案类似于其他答案,但既然你说你需要"一些随机元素",这将更具性能:

public IEnumerable<TValue> RandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
    Random rand = new Random();
    List<TValue> values = Enumerable.ToList(dict.Values);
    int size = dict.Count;
    while(true)
    {
        yield return values[rand.Next(size)];
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用此方法:

Dictionary<string, object> dict = GetDictionary();
foreach (object value in RandomValues(dict).Take(10))
{
    Console.WriteLine(value);
}
Run Code Online (Sandbox Code Playgroud)

这比其他响应(包括yshuditelu的响应)有性能改进.

  1. 每次要获取新的随机值时,都不必创建所有字典元素的新集合.如果您的词典中包含很多元素,这是一个非常重要的事情.
  2. 每次获取随机值时,都不必基于Dictionary的键执行查找.没有#1那么大,但它的速度仍然超过这个速度的两倍.

我的测试表明,在字典中有1000个对象,这种方法比其他建议的方法快70倍.

  • 很好地抓住'随机物品的数量'.虽然如果你真的在寻找perf,你可能不想要键查找 - 构建KeyValuePair对象列表并使用它.基本的想法是可靠的.+1 (2认同)
  • 好的实现。可能值得注意的是,这可能多次返回字典中的同一元素(这可能是或可能不是原始海报所寻找的行为)。 (2认同)

Rob*_*ino 17

从你的字典......

Dictionary<string, int> dict = new Dictionary<string, object>()
Run Code Online (Sandbox Code Playgroud)

你可以创建一个完整的密钥列表 ......

List<string> keyList = new List<string>(dict.Keys);
Run Code Online (Sandbox Code Playgroud)

然后从列表中选择一个随机密钥.

Random rand = new Random();
string randomKey = keyList[rand.Next(keyList.Count)];
Run Code Online (Sandbox Code Playgroud)

然后只需返回匹配该键的随机对象.

return dict[randomKey];
Run Code Online (Sandbox Code Playgroud)


Str*_*ior 12

我的另一个答案对于这个问题是正确的,并且在许多情况下都很有用,例如从自定义骰子获取滚动信息(每个骰子的滚动是随机的,独立于其他骰子).但是,你的评论听起来好像你可能希望得到一系列"独特"的元素Dictionary,有点像从牌组中发牌.一旦发卡,你再也不想看到同一张卡,直到重新洗牌.在这种情况下,最好的策略将取决于您正在做什么.

如果你只从一个大的元素中获得一些元素Dictionary,那么你应该能够调整我的另一个答案,每次检索一个新元素时从列表中删除随机元素.您可能还希望将列表设置为a LinkedList,因为即使按索引查找项目速度较慢,从中间删除元素也要便宜得多.这个代码会有点复杂,所以如果你愿意为了简单而牺牲一些性能,你可以这样做:

public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
    Random rand = new Random();
    Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>(dict);
    while(values.Count > 0)
    {
        TKey randomKey = values.Keys.ElementAt(rand.Next(0, values.Count));  // hat tip @yshuditelu 
        TValue randomValue = values[randomKey];
        values.Remove(randomKey);
        yield return randomValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

另一方面,如果你计划从你的词典中提取大量元素(即处理超过你的"套牌"的log(n)),你最好只是先洗牌你的整个套牌,然后从顶部拉出:

public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
    // Put the values in random order
    Random rand = new Random();
    LinkedList<TValue> values = new LinkedList<TValue>(from v in dict.Values
                                                       orderby rand.Next()
                                                       select v);
    // Remove the values one at a time
    while(values.Count > 0)
    {
        yield return values.Last.Value;
        values.RemoveLast();
    }
}
Run Code Online (Sandbox Code Playgroud)

对于简单的洗牌代码,可以转到ookii.org.如果这仍然不是你想要的,也许你可以开始一个新的问题,详细了解你正在尝试做什么.