web-dev-qa-db-ja.com

辞書からのランダムなエントリ

C#の辞書からランダムなエントリを取得する最良の方法は何ですか?

ページに表示するには、辞書からいくつかのランダムなオブジェクトを取得する必要がありますが、辞書はインデックスでアクセスできないため、次のものを使用できません。

Random Rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[Rand.Next()];

助言がありますか?

49
Ed James

ジェネリックを使用するために更新され、さらに高速になり、このオプションが高速である理由の説明付き

この回答は他の回答と似ていますが、「いくつかのランダムな要素」が必要だと言ったので、これはよりパフォーマンスが向上します。

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)];
    }
}

この方法は次のように使用できます。

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

これにより、他の応答(yshuditeluの応答を含む)よりもパフォーマンスが向上します。

  1. 新しいランダム値を取得するたびに、辞書のすべての要素の新しいコレクションを作成する必要はありません。辞書に多くの要素が含まれている場合、これは非常に重要です。
  2. ランダムな値を取得するたびに、辞書のキーに基づいてルックアップを実行する必要はありません。それほど大きな問題ではありませんが、この方法では2倍以上高速です。

私のテストでは、辞書に1000個のオブジェクトがある場合、このメソッドは他の推奨されるメソッドよりも約70倍速くなることが示されています。

42

.net 3.5を使用している場合、Enumerableには拡張メソッド ElementAt があり、これを行うことができます。

return dict.ElementAt(Rand.Next(0, dict.Count)).Value;
47
Timothy Carter

辞書から...

Dictionary<string, int> dict = new Dictionary<string, object>()

完全なキーのリスト ...を作成できます.

List<string> keyList = new List<string>(dict.Keys);

次に、リストからランダムキーを選択にします。

Random Rand = new Random();
string randomKey = keyList[Rand.Next(keyList.Count)];

次に、単純にランダムオブジェクトを返すそのキーに一致します。

return dict[randomKey];
17
Robert Cartaino

私の他の答えは質問に対して正しいものであり、カスタムサイコロからロール情報を取得するなど、多くの場合に役立ちます(各サイコロのロールはランダムで、他のサイコロとは無関係です)。しかし、あなたのコメントは、Dictionaryから一連の「ユニークな」要素を取得したいと思っているように聞こえます。カードが配られたら、再シャッフルするまで同じカードを見たくありません。その場合、最善の戦略はあなたが何をしているのかによって異なります。

大きなDictionaryからいくつかの要素のみを取得している場合、新しい要素が取得されるたびにリストからランダムな要素を削除して、他の答えを調整できるはずです。おそらく、リストを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;
    }
}

一方、辞書からかなりの数の要素を取り出すことを計画している場合(つまり、「デッキ」の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();
    }
}

クレジットは、単純なシャッフルコードの ookii.org になります。これがまだあなたが探していたものではない場合は、おそらくあなたがしようとしていることについての詳細を含む新しい質問を始めることができます。

12

何かのようなもの:

Random Rand = new Random();
Dictionary dict = GetDictionary();
var k = dict.Keys.ToList()[Rand.Next(dict.Count)];
return dict[k];
3
Henk Holterman

これはそれほど高速ではありませんが、動作するはずです。

Random Rand = new Random();
Dictionary dict = GetDictionary();
return dict.Skip(Rand.Next(dict.Count)).First().Value;
2
Jonathan Rupp

簡単な解決策は、 ToList() 拡張メソッドを使用し、リストのインデックスを使用することです。

値またはキー(キー/値のペアではない)だけが必要な場合は、辞書からこれらのコレクションを返し、 ToList() も使用します。

        Random Rand = new Random();
        Dictionary<string, object> dict = GetDictionary();
        var k = dict.ToList()[Rand.Next(dict.Count)];
        // var k = dict.Values.ToList()[Rand.Next(dict.Count)];
        // var k = dict.Keys.ToList()[Rand.Next(dict.Count)];

        Console.WriteLine("Random dict pair {0} = {1}", k.Key, k.Value);
1
bruno conde

唯一の方法は、最初にKeyValuePairsの個別のリストを作成することだと思います。

0
Groo
public static class DictionaryExtensions
{
    public static TKey[] Shuffle<TKey, TValue>(
       this System.Collections.Generic.Dictionary<TKey, TValue> source)
    {
        Random r = new Random();
        TKey[] wviTKey = new TKey[source.Count];
        source.Keys.CopyTo(wviTKey, 0);

        for (int i = wviTKey.Length; i > 1; i--)
        {
            int k = r.Next(i);
            TKey temp = wviTKey[k];
            wviTKey[k] = wviTKey[i - 1];
            wviTKey[i - 1] = temp;
        }

        return wviTKey;
    }
}

サンプル

            // Using
            System.Collections.Generic.Dictionary<object, object> myDictionary = new System.Collections.Generic.Dictionary<object, object>();
            // myDictionary.Add(myObjectKey1, myObjectValue1); // Sample
            // myDictionary.Add(myObjectKey2, myObjectValue2); // Sample
            // myDictionary.Add(myObjectKey3, myObjectValue3); // Sample
            // myDictionary.Add(myObjectKey4, myObjectValue4); // Sample

            // var myShufledKeys = myDictionary.Shuffle(); // Sample
            // var myShufledValue = myDictionary[myShufledKeys[0]]; // Sample

            // Easy Sample
            var myObjects = System.Linq.Enumerable.Range(0, 4);
            foreach(int i in myObjects)
                myDictionary.Add(i, string.Format("myValueObjectNumber: {0}", i));

            var myShufledKeys = myDictionary.Shuffle();
            var myShufledValue = myDictionary[myShufledKeys[0]];
0
user3245067