リスト内のランダムなアイテムにアクセスする方法は?


233

私はArrayListを持っていて、ボタンをクリックして、そのリストから文字列をランダムに選択してメッセージボックスに表示できるようにする必要があります。

これを行うにはどうすればよいですか?

回答:


404
  1. Randomどこかにクラスのインスタンスを作成します。乱数が必要になるたびに新しいインスタンスを作成しないことが非常に重要であることに注意してください。生成された数値を均一にするために、古いインスタンスを再利用する必要があります。あなたはstaticどこかにフィールドを持つことができます(スレッドセーフの問題に注意してください):

    static Random rnd = new Random();
  2. 掲載Random中のアイテムの数を最大にあなたの乱数を与えるためにインスタンスをArrayList

    int r = rnd.Next(list.Count);
  3. 文字列を表示します。

    MessageBox.Show((string)list[r]);

数字が繰り返されないようにこれを変更する良い方法はありますか?たとえば、ランダムに1枚ずつカードのデッキをシャッフルしたいが、同じカードを2度選択できないことは明らかです。
AdamMc331 2014

7
@ McAdam331フィッシャーイェーツのシャッフルアルゴリズムを検索:en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
要素の最大値へのアクセスを回避するために、これは「rnd.Next(list.Count)」ではなく「rnd.Next(list.Count-1)」にする必要があります。
B.クレイシャノン

22
@ B.ClayShannonいいえNext(max)。呼び出しの上限は排他的です。
Mehrdad Afshari、2016

1
リストが空の場合はどうですか?
tsu1980

137

私は通常、この拡張メソッドの小さなコレクションを使用します。

public static class EnumerableExtension
{
    public static T PickRandom<T>(this IEnumerable<T> source)
    {
        return source.PickRandom(1).Single();
    }

    public static IEnumerable<T> PickRandom<T>(this IEnumerable<T> source, int count)
    {
        return source.Shuffle().Take(count);
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.OrderBy(x => Guid.NewGuid());
    }
}

強く型付けされたリストの場合、これにより次のように記述できます。

var strings = new List<string>();
var randomString = strings.PickRandom();

ArrayListだけの場合は、キャストできます。

var strings = myArrayList.Cast<string>();

それらの複雑さは何ですか?IEnumerableの怠惰な性質は、それがO(N)ではないことを意味しますか?
Dave Hillier、

17
この答えは、乱数を選択するたびにリストをシャッフルし直します。特に大きなリストの場合、ランダムなインデックス値を返す方がはるかに効率的です。これをPickRandomで使用する return list[rnd.Next(list.Count)];
swax

このdoesntのは...それはリストが十分な大きさであれば、まだ効率のために良いことではないかもしれない実際には別のリストにし、元のリストをシャッフル
nawfal

.OrderBy(。)は別のリストを作成しません-順序付けされた方法で元のリストを反復処理するIEnumerable <T>型のオブジェクトを作成します。
JohanTidén2013

5
GUID生成アルゴリズムは予測できませんが、ランダムではありません。Random代わりにのインスタンスを静的状態で保持することを検討してください。

90

できるよ:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()

綺麗な。ASP.NET MVC 4.5では、リストを使用して、これを次のように変更する必要がありました:list.OrderBy(x => Guid.NewGuid())。FirstOrDefault();
アンディブラウン

3
ほとんどの場合問題にはなりませんが、これはおそらくrnd.Nextを使用するよりもはるかに遅くなります。OTOHそれはリストだけでなくIEnumerable <T>でも機能します。
水溶性魚、2015年

12
それがどれほどランダムかわかりません。GUIDは一意であり、ランダムではありません。
2016年

1
私はこの回答のより拡張されたバージョンと@sensitivefishのコメントが、同様の質問に対するこの回答(と私のコメント)にうまくまとめられている思います。
ネオ

23

Randomインスタンスを作成します。

Random rnd = new Random();

ランダムな文字列を取得します。

string s = arraylist[rnd.Next(arraylist.Count)];

ただし、これを頻繁に行う場合は、Randomオブジェクトを再利用する必要があることに注意してください。それをクラスの静的フィールドとして配置し、一度だけ初期化されてからアクセスされるようにします。


20

または、次のような単純な拡張クラス:

public static class CollectionExtension
{
    private static Random rng = new Random();

    public static T RandomElement<T>(this IList<T> list)
    {
        return list[rng.Next(list.Count)];
    }

    public static T RandomElement<T>(this T[] array)
    {
        return array[rng.Next(array.Length)];
    }
}

次に呼び出すだけです:

myList.RandomElement();

アレイでも機能します。

OrderBy()大きなコレクションの場合は高額になる可能性があるので、電話は避けます。List<T>この目的のために、または配列のようなインデックス付きコレクションを使用します。


3
.NETの配列はすでに実装されているIListため、2番目のオーバーロードは不要です。

3

何故なの:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}

2
ArrayList ar = new ArrayList();
        ar.Add(1);
        ar.Add(5);
        ar.Add(25);
        ar.Add(37);
        ar.Add(6);
        ar.Add(11);
        ar.Add(35);
        Random r = new Random();
        int index = r.Next(0,ar.Count-1);
        MessageBox.Show(ar[index].ToString());

3
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
gunr2171

3
ドキュメントによると、「maxValueは乱数の排他的な上限」であるためmaxValue、method のパラメータはNext、リスト内の要素の数であり、マイナス1であってはなりません。
David FerenczyRogožan2015年

1

私はこのExtensionMethodをしばらく使用しています:

public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int count)
{
    if (count <= 0)
      yield break;
    var r = new Random();
    int limit = (count * 10);
    foreach (var item in list.OrderBy(x => r.Next(0, limit)).Take(count))
      yield return item;
}

ランダムクラスのインスタンスを追加するのを忘れていました
bafsar 2017年

1

別のアプローチをお勧めします。リスト内の項目の順序が抽出時に重要ではない場合(各項目は一度だけ選択するList必要がある場合)、代わりにConcurrentBag、スレッドセーフで順序付けられていないコレクションを使用できます。オブジェクト:

var bag = new ConcurrentBag<string>();
bag.Add("Foo");
bag.Add("Boo");
bag.Add("Zoo");

EventHandler:

string result;
if (bag.TryTake(out result))
{
    MessageBox.Show(result);
}

TryTake順不同コレクションから「ランダム」オブジェクトを抽出しようとします。


0

1つではなく、もっと多くのアイテムが必要でした。だから、私はこれを書いた:

public static TList GetSelectedRandom<TList>(this TList list, int count)
       where TList : IList, new()
{
    var r = new Random();
    var rList = new TList();
    while (count > 0 && list.Count > 0)
    {
        var n = r.Next(0, list.Count);
        var e = list[n];
        rList.Add(e);
        list.RemoveAt(n);
        count--;
    }

    return rList;
}

これにより、次のようにランダムに必要な数の要素を取得できます。

var _allItems = new List<TModel>()
{
    // ...
    // ...
    // ...
}

var randomItemList = _allItems.GetSelectedRandom(10); 

0

JSONファイルからランダムに国名を印刷します。
モデル:

public class Country
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

実装:

string filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\")) + @"Data\Country.json";
            string _countryJson = File.ReadAllText(filePath);
            var _country = JsonConvert.DeserializeObject<List<Country>>(_countryJson);


            int index = random.Next(_country.Count);
            Console.WriteLine(_country[index].Name);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.