【C#】リストから要素をランダムにN個取得する

今回は、リストから要素をN個取得する実装方法の紹介をしたいと思います。

いちど選んだ要素はもう使わない + データを取り出した後に元のリストが変化しないように実装していきます。

確認環境

実装環境は以下の通りです。

  • Visual Studio 2022 + .NET Core3.1(C#6.0)
  • Unity 2022.3.5f1

使い方

まずは使うとどうなるか紹介します。

要素をランダムにN個取得する GetRandomN メソッドと、リストの内容を全てランダムに取得する Shuffle メソッドの 2つがあります。

// テスト用のデータの作成。100~199までの整数のリスト
IList<int> source = Enumerable.Range(100, 100).ToList();

// ランダムに5つ値を取得する
foreach (int value in source.GetRandomN(5))
{
    Console.WriteLine(value);
    // 144
    // 191
    // 178
    // 175
    // 159
}

// 全部の値をランダムに取得する
foreach (var value in source.Shuffle())
{
    Console.WriteLine(value);
    // 126
    // 192
    // 104
    // 182
    // 180
    // ... (以下略
}

RandomUtilクラス

ランダムに要素を選択する処理を実装しているクラスです。

// RandomUtil.cs

using System;
using System.Collections.Generic;
using System.Linq;

public static class IListExtensions
{
    // 指定したリスト内からランダムでN個のデータを取得する
    public static IEnumerable<T> GetRandomN<T>(this IList<T> collection, int n)
    {
        if (n > collection.Count)
        {
            throw new ArgumentOutOfRangeException("リストの要素数よりnが大きいです。");
        }

        var indexList = new List<int>(collection.Count);
        for (int p = 0; p < collection.Count; p++) indexList.Add(p);

        var random = new Random();
        for (int i = 0; i < n; i++)
        {
            int index = random.Next(0, indexList.Count);
            int value = indexList[index];
            indexList.RemoveAt(index);
            yield return collection[value];
        }
    }

    // リストの内容をランダムに全て取得する
    public static IEnumerable<T> Shuffle<T>(this IList<T> collection)
    {
        return collection.GetRandomN(collection.Count);
    }
}

もし Unity 環境の場合以下のように GetRandomN メソッド書き換えると Unity 用の処理になります。

// Unityの場合以下のように書き換える
public static IEnumerable<T> GetRandomN<T>(this IList<T> collection, int n)
{
    if (n > collection.Count)
    {
        throw new ArgumentOutOfRangeException("リストの要素数よりnが大きいです。");
    }

    var indexList = new List<int>(collection.Count);
    for (int p = 0; p < collection.Count; p++) indexList.Add(p);

    for (int i = 0; i < n; i++)
    {
        int index = UnityEngine.Random.Range(0, indexList.Count); // ★ここ
        int value = indexList[index];
        indexList.RemoveAt(index);
        yield return collection[value];
    }
}

関連記事

takap-tech.com