C# Randomの使い方完全ガイド|乱数生成・範囲指定・重複回避・Seedまで解説

はじめに

C#で乱数を生成したいときに使う代表的なクラスがSystem.Randomです。サイコロの目、抽選、ゲームの敵出現、配列からランダムに1件選ぶ処理など、幅広い場面で利用できます。

ただし、Randomには「上限値は含まれない」「短時間に何度もnew Random()すると同じ値が出ることがある」「セキュリティ用途には使えない」といった重要な注意点があります。

この記事では、C# Randomの基本的な使い方から、範囲指定、重複回避、Seed、Random.Shared、安全な乱数生成まで、実用コードを交えて解説します。

1. C#のRandomとは?乱数生成でできること

1-1. Randomクラスの基本概要

Randomは、C#で疑似乱数を生成するためのクラスです。名前空間はSystemに含まれているため、通常は次のように使います。

C#
Random random = new Random();

int number = random.Next();
Console.WriteLine(number);

Randomでは、主に次のような乱数を生成できます。

C#
Random random = new Random();

int a = random.Next(); // 0以上の整数
int b = random.Next(10); // 0以上10未満
int c = random.Next(1, 11); // 1以上11未満
double d = random.NextDouble(); // 0.0以上1.0未満

Microsoftのドキュメントでも、Randomは疑似乱数ジェネレーターを表すクラスとして説明されています。疑似乱数は数学的なアルゴリズムで生成されるため完全なランダムではありませんが、一般的なアプリケーションでは十分実用的です。

1-2. 疑似乱数とは何か

疑似乱数とは、一見ランダムに見える数値列を、決まったアルゴリズムによって生成したものです。

コンピューターは基本的に決定的な処理を行うため、何の条件もなく完全に予測不能な値を作ることは得意ではありません。そこで、Randomのような疑似乱数生成器を使って、ランダムに見える値を作ります。

重要なのは、疑似乱数は「再現できる」という点です。Seedを同じにすると、同じ乱数列を生成できます。この特徴は、テストやデバッグではメリットになります。

1-3. C#でRandomを使う主な場面

Randomは、次のような場面でよく使われます。

C#
Random random = new Random();

// サイコロ
int dice = random.Next(1, 7);

// 抽選
string[] prizes = { "A賞", "B賞", "C賞" };
string prize = prizes[random.Next(prizes.Length)];

// 確率判定
bool isRare = random.NextDouble() < 0.05; // 5%の確率

// ランダムな待機時間
int delayMs = random.Next(1000, 5000);

ゲーム、シミュレーション、サンプルデータ作成、UI演出、テスト用データ生成など、セキュリティが関係しない用途であればRandomは扱いやすい選択肢です。

1-4. Randomを使う前に知っておきたい注意点

Randomを使う前に、次の点を押さえておきましょう。

Next(minValue, maxValue)maxValueは含まれません。たとえばrandom.Next(1, 10)は1〜9を返し、10は返しません。

また、Randomは暗号学的に安全な乱数ではありません。パスワード、認証トークン、リセットURL、APIキーなどを生成する場合は、RandomNumberGeneratorを使います。Microsoftのドキュメントでも、ランダムパスワードのような暗号学的に安全な乱数にはRandomNumberGeneratorの利用が案内されています。

2. C# Randomの基本的な使い方

2-1. Randomクラスのインスタンスを作成する

基本形は次のとおりです。

C#
Random random = new Random();

C# 9以降の書き方であれば、次のようにも書けます。

C#
Random random = new();

.NET 6以降では、共有インスタンスであるRandom.Sharedも使えます。

C#
int number = Random.Shared.Next(1, 101);

通常の小規模なコードではnew Random()でも問題ありませんが、アプリ全体で乱数を何度も使う場合は、同じインスタンスを使い回すのが基本です。

2-2. Next()で整数の乱数を生成する

引数なしのNext()は、0以上int.MaxValue未満の整数を返します。

C#
Random random = new Random();

int value = random.Next();

Console.WriteLine(value);

非常に大きな範囲から整数を取得したい場合に使えますが、実務では範囲を指定するNext(maxValue)Next(minValue, maxValue)の方がよく使われます。

2-3. Next(maxValue)で0以上maxValue未満の乱数を生成する

Next(maxValue)は、0以上maxValue未満の整数を返します。

C#
Random random = new Random();

int value = random.Next(10);

Console.WriteLine(value);

この場合、出力される可能性がある値は次のとおりです。

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

10は含まれません。

2-4. Next(minValue, maxValue)で範囲指定する

任意の範囲を指定したい場合は、Next(minValue, maxValue)を使います。

C#
Random random = new Random();

int value = random.Next(1, 11);

Console.WriteLine(value);

このコードでは、1以上11未満、つまり1〜10の整数が生成されます。

2-5. maxValueは含まれない点に注意する

Next(minValue, maxValue)の第2引数は「含まれない上限」です。Microsoftのドキュメントでも、Next(Int32, Int32)の上限は排他的、つまり返される範囲に含まれないと説明されています。

たとえば、1〜100を生成したい場合は、次のように書きます。

C#
int value = random.Next(1, 101);

間違いやすい例は次のコードです。

C#
// 1〜100のつもりだが、実際は1〜99
int value = random.Next(1, 100);

上限を含めたい場合は、maxValue + 1を指定するのが基本です。

2-6. 実行するたびに結果が変わるサンプルコード

C#
Random random = new Random();

for (int i = 0; i < 5; i++)
{
int value = random.Next(1, 101);
Console.WriteLine(value);
}

実行例は次のようになります。

12
87
34
5
60

再度実行すると、多くの場合は異なる結果になります。ただし、Seedを固定した場合は同じ乱数列になります。

3. 範囲を指定して乱数を生成する方法

3-1. 1〜10の乱数を生成する

1〜10の整数をランダムに取得するには、上限に11を指定します。

C#
Random random = new Random();

int value = random.Next(1, 11);

Console.WriteLine(value);

サイコロのように1から始まる値を扱う場合は、この書き方をよく使います。

3-2. 0〜99の乱数を生成する

0〜99を取得したい場合は、Next(100)を使います。

C#
Random random = new Random();

int value = random.Next(100);

Console.WriteLine(value);

Next(100)は0以上100未満なので、結果は0〜99です。

3-3. マイナス値を含む範囲の乱数を生成する

Next(minValue, maxValue)では、マイナス値を含む範囲も指定できます。

C#
Random random = new Random();

int value = random.Next(-10, 11);

Console.WriteLine(value);

このコードでは、-10〜10の整数が生成されます。

3-4. 配列やリストのランダムな要素を取得する

配列からランダムに1件取得する場合は、インデックスをランダムに生成します。

C#
Random random = new Random();

string[] fruits = { "Apple", "Banana", "Orange", "Grape" };

int index = random.Next(fruits.Length);
string selected = fruits[index];

Console.WriteLine(selected);

リストでも考え方は同じです。

C#
Random random = new Random();

List<string> names = new() { "Alice", "Bob", "Charlie" };

string selected = names[random.Next(names.Count)];

Console.WriteLine(selected);

random.Next(array.Length)は0以上Length未満になるため、配列のインデックスと相性が良い書き方です。

3-5. bool値をランダムに生成する

bool値をランダムに生成したい場合は、0または1を生成して判定します。

C#
Random random = new Random();

bool result = random.Next(2) == 0;

Console.WriteLine(result);

確率を変えたい場合は、NextDouble()を使うと便利です。

C#
Random random = new Random();

// 30%の確率でtrue
bool result = random.NextDouble() < 0.3;

3-6. 指定範囲でよくあるミスと修正例

よくあるミスは、上限値を含むと勘違いすることです。

C#
// NG: 1〜10のつもりだが、10は出ない
int value = random.Next(1, 10);

正しくは次のようにします。

C#
// OK: 1〜10
int value = random.Next(1, 11);

また、第1引数が第2引数以上になると例外が発生します。

C#
// NG
int value = random.Next(10, 1);

範囲を変数で扱う場合は、事前にチェックすると安全です。

C#
int min = 10;
int max = 1;

if (min >= max)
{
throw new ArgumentException("minはmaxより小さくしてください。");
}

int value = random.Next(min, max);

4. 小数・double・floatの乱数を生成する方法

4-1. NextDouble()で0.0以上1.0未満の乱数を生成する

小数の乱数を生成するには、NextDouble()を使います。

C#
Random random = new Random();

double value = random.NextDouble();

Console.WriteLine(value);

NextDouble()は、0.0以上1.0未満のdouble値を返します。

4-2. 任意の範囲のdouble乱数を生成する

たとえば、10.0〜20.0の範囲で小数を生成したい場合は、次のように計算します。

C#
Random random = new Random();

double min = 10.0;
double max = 20.0;

double value = min + random.NextDouble() * (max - min);

Console.WriteLine(value);

汎用メソッドにすると便利です。

C#
static double NextDouble(Random random, double min, double max)
{
return min + random.NextDouble() * (max - min);
}

使い方は次のとおりです。

C#
Random random = new Random();

double value = NextDouble(random, -5.0, 5.0);

Console.WriteLine(value);

4-3. float型の乱数を生成する

NextDouble()doubleを返すため、floatで扱いたい場合はキャストします。

C#
Random random = new Random();

float value = (float)random.NextDouble();

Console.WriteLine(value);

任意の範囲のfloatを生成する例です。

C#
Random random = new Random();

float min = 1.5f;
float max = 3.5f;

float value = min + (float)random.NextDouble() * (max - min);

Console.WriteLine(value);

4-4. 小数点以下の桁数を丸める方法

小数点以下の桁数をそろえたい場合は、Math.Roundを使います。

C#
Random random = new Random();

double value = random.NextDouble() * 100;
double rounded = Math.Round(value, 2);

Console.WriteLine(rounded);

金額や表示用の数値であれば、丸めた値を使うよりも、表示時にフォーマットする方が適している場合もあります。

C#
Console.WriteLine(value.ToString("F2"));

4-5. 整数と小数のRandomの使い分け

整数が必要な場合はNext()、小数が必要な場合はNextDouble()を使います。

C#
Random random = new Random();

// 整数
int hp = random.Next(50, 101);

// 小数
double rate = random.NextDouble();

ID、個数、インデックス、サイコロの目などは整数が向いています。一方、確率、倍率、座標、重み付けなどには小数が向いています。

5. 重複しない乱数を生成する方法

5-1. Randomは重複する可能性がある

Randomは、呼び出すたびにランダムな値を返しますが、重複を自動で避けるわけではありません。

C#
Random random = new Random();

for (int i = 0; i < 10; i++)
{
Console.WriteLine(random.Next(1, 6));
}

このコードでは1〜5の値が10回出力されるため、当然重複します。

5-2. HashSetで重複を回避する方法

一定範囲から重複しない値を指定個数だけ取得したい場合は、HashSet<int>が便利です。

C#
Random random = new Random();
HashSet<int> numbers = new HashSet<int>();

while (numbers.Count < 5)
{
int value = random.Next(1, 11);
numbers.Add(value);
}

foreach (int number in numbers)
{
Console.WriteLine(number);
}

この例では、1〜10の中から重複なしで5個取得します。

5-3. Listをシャッフルして重複なしで取り出す方法

範囲内の値をすべてリスト化し、シャッフルして先頭から取り出す方法もあります。

C#
Random random = new Random();

List<int> numbers = Enumerable.Range(1, 10).ToList();

numbers = numbers.OrderBy(_ => random.Next()).ToList();

List<int> selected = numbers.Take(5).ToList();

foreach (int number in selected)
{
Console.WriteLine(number);
}

ただし、OrderBy(_ => random.Next())は簡単に書ける一方で、本格的なシャッフルには次のFisher-Yatesシャッフルの方が適しています。

5-4. Fisher-Yatesシャッフルでランダム並び替えする

Fisher-Yatesシャッフルは、リストを偏り少なくランダムに並び替える定番アルゴリズムです。

C#
static void Shuffle<T>(IList<T> list, Random random)
{
for (int i = list.Count - 1; i > 0; i--)
{
int j = random.Next(i + 1);

(list[i], list[j]) = (list[j], list[i]);
}
}

使い方は次のとおりです。

C#
Random random = new Random();

List<int> numbers = Enumerable.Range(1, 10).ToList();

Shuffle(numbers, random);

foreach (int number in numbers)
{
Console.WriteLine(number);
}

5-5. 重複なし乱数を作るときの注意点

重複なしで乱数を作る場合は、取得したい個数が範囲の数を超えないようにする必要があります。

たとえば、1〜10の範囲には10個の値しかありません。その中から重複なしで20個取得することはできません。

C#
int min = 1;
int max = 10;
int count = 20;

int rangeSize = max - min + 1;

if (count > rangeSize)
{
throw new ArgumentException("取得個数が範囲内の要素数を超えています。");
}

5-6. 範囲より多い個数を取得しようとした場合の対処法

範囲より多い個数が必要な場合は、次のいずれかを選びます。

C#
// 対処1: 範囲を広げる
int value = random.Next(1, 101);

// 対処2: 重複を許可する
int value2 = random.Next(1, 11);

// 対処3: 取得個数を範囲内に制限する
int count = Math.Min(requestedCount, rangeSize);

「重複してもよい抽選」なのか、「一度出た値は出してはいけない抽選」なのかを先に決めることが重要です。

6. Seedを指定して同じ乱数列を再現する方法

6-1. Seedとは何か

Seedとは、疑似乱数生成の開始点になる値です。同じSeedを指定すると、同じ乱数列を再現できます。

これは、ランダムな処理を含むテスト、ゲームのリプレイ、シミュレーションの再現などで役立ちます。

6-2. new Random(seed)の使い方

Seedを指定するには、Randomのコンストラクターに整数を渡します。

C#
Random random = new Random(123);

Console.WriteLine(random.Next(1, 101));
Console.WriteLine(random.Next(1, 101));
Console.WriteLine(random.Next(1, 101));

Random(Int32)は指定したSeedでRandomインスタンスを初期化します。Microsoftのドキュメントでも、同じSeedを使うと同じ乱数列を生成できることが説明されています。

6-3. 同じSeedで同じ結果になるサンプルコード

C#
Random random1 = new Random(100);
Random random2 = new Random(100);

for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{random1.Next(1, 101)} / {random2.Next(1, 101)}");
}

出力例は次のようになります。

97 / 97
16 / 16
67 / 67
91 / 91
36 / 36

同じSeedを使っているため、同じ順番で値が生成されます。

6-4. テストやデバッグでSeedを使うメリット

Seedを固定すると、ランダムな処理を再現できます。

C#
Random random = new Random(42);

たとえば、ゲームで特定のマップ生成結果を再現したい場合や、テストで失敗したランダムケースをもう一度確認したい場合に便利です。

C#
int seed = 42;
Random random = new Random(seed);

Console.WriteLine($"Seed: {seed}");

ログにSeedを出しておくと、問題が発生したケースを再現しやすくなります。

6-5. Seedを指定しない場合の動作

Seedを指定しない場合は、既定のSeedが使われます。

C#
Random random = new Random();

通常はこの書き方で問題ありません。毎回異なる結果がほしいアプリケーションでは、Seedを固定しない方が自然です。

6-6. Seedを使うときの注意点

Seedを固定すると、毎回同じ結果になります。これはテストでは便利ですが、本番の抽選やゲーム演出で毎回同じ結果になると困る場合があります。

C#
// 毎回同じ結果になる
Random random = new Random(1);

再現性が必要な場面だけSeedを指定し、通常のランダム処理ではSeedなし、またはRandom.Sharedを使うのがおすすめです。

7. Randomで同じ値が出る原因と対策

7-1. 短時間にnew Random()を繰り返すと起きる問題

古い.NET Framework環境では、短時間にnew Random()を繰り返すと同じSeedが使われ、同じような乱数列になることがあります。Microsoftのドキュメントでは、.NET Frameworkにおいて短い間隔で複数のRandomを作ると同じシーケンスを生成し得るため、単一のRandomを使うことが推奨されています。

C#
// 避けたい例
for (int i = 0; i < 10; i++)
{
Random random = new Random();
Console.WriteLine(random.Next(1, 101));
}

このようにループ内で毎回new Random()する書き方は避けましょう。

7-2. Randomインスタンスは使い回すのが基本

正しくは、Randomインスタンスを一度作成して使い回します。

C#
Random random = new Random();

for (int i = 0; i < 10; i++)
{
Console.WriteLine(random.Next(1, 101));
}

この書き方なら、1つの乱数生成器から順番に値を取り出せます。

7-3. static Randomを使う方法

アプリ内の複数箇所で乱数を使う場合は、staticフィールドにする方法があります。

C#
public static class RandomProvider
{
private static readonly Random Random = new Random();

public static int Next(int minValue, int maxValue)
{
return Random.Next(minValue, maxValue);
}
}

使い方です。

C#
int value = RandomProvider.Next(1, 101);

ただし、複数スレッドから同時に使う場合はスレッドセーフ性に注意が必要です。

7-4. .NET 6以降はRandom.Sharedを使う

.NET 6以降では、Random.Sharedを使うと簡単です。

C#
int value = Random.Shared.Next(1, 101);
double rate = Random.Shared.NextDouble();

Random.Sharedは、複数スレッドから同時に使えるスレッドセーフなRandomインスタンスとして提供されています。

7-5. ループ内でnew Random()してはいけない理由

ループ内でnew Random()すると、次の問題が起きやすくなります。

C#
for (int i = 0; i < 100; i++)
{
Random random = new Random();
int value = random.Next(1, 10);
}

問題点は、インスタンス生成のコストが無駄に増えることと、環境によっては同じような乱数列になりやすいことです。

次のように、ループの外で1回だけ作成します。

C#
Random random = new Random();

for (int i = 0; i < 100; i++)
{
int value = random.Next(1, 10);
}

7-6. 同じ乱数ばかり出るときのチェックポイント

同じ値ばかり出る場合は、次を確認しましょう。

C#
// チェック1: ループ内でnew Random()していないか
// チェック2: Seedを固定していないか
// チェック3: 範囲が狭すぎないか
// チェック4: maxValueを誤解していないか
// チェック5: マルチスレッドで同じRandomを安全に扱っているか

特に多いのは、Seedを固定したまま本番処理に使っているケースです。

C#
// 毎回同じ乱数列になる
Random random = new Random(123);

8. Random.Sharedとスレッドセーフな乱数生成

8-1. Random.Sharedとは何か

Random.Sharedは、共有されたRandomインスタンスを取得するプロパティです。

C#
int value = Random.Shared.Next(1, 101);

従来のようにnew Random()を自分で管理しなくても、簡単に乱数を生成できます。

8-2. Random.Sharedの基本的な使い方

整数の乱数です。

C#
int number = Random.Shared.Next(1, 11);

小数の乱数です。

C#
double value = Random.Shared.NextDouble();

配列からランダムに選ぶ例です。

C#
string[] colors = { "Red", "Blue", "Green" };

string selected = colors[Random.Shared.Next(colors.Length)];

Console.WriteLine(selected);

8-3. 従来のRandomとの違い

従来の書き方です。

C#
Random random = new Random();
int value = random.Next(1, 101);

Random.Sharedを使う書き方です。

C#
int value = Random.Shared.Next(1, 101);

小規模な処理ではどちらでも書けますが、.NET 6以降で単純に乱数を使いたい場合は、Random.Sharedが手軽です。

8-4. マルチスレッド環境での注意点

通常のRandomインスタンスは、マルチスレッドで同時に呼び出す場合に注意が必要です。Microsoftのドキュメントでも、Randomオブジェクトはスレッドセーフではなく、複数スレッドから使う場合は同期が必要だと説明されています。

従来のRandomを複数スレッドで使う場合は、lockを使う方法があります。

C#
public class ThreadSafeRandom
{
private readonly Random _random = new Random();
private readonly object _lock = new object();

public int Next(int minValue, int maxValue)
{
lock (_lock)
{
return _random.Next(minValue, maxValue);
}
}
}

.NET 6以降なら、まずはRandom.Sharedを検討するとよいでしょう。

8-5. Random.Sharedを使うべきケース

Random.Sharedは、次のようなケースに向いています。

C#
int dice = Random.Shared.Next(1, 7);
double probability = Random.Shared.NextDouble();
string item = items[Random.Shared.Next(items.Count)];

特別なSeed管理が不要で、セキュリティ用途でもなく、単にランダムな値がほしい場合に適しています。

8-6. .NET Frameworkでの代替方法

.NET FrameworkではRandom.Sharedが使えないため、static Randomlockを組み合わせる方法があります。

C#
public static class RandomUtil
{
private static readonly Random Random = new Random();
private static readonly object LockObject = new object();

public static int Next(int minValue, int maxValue)
{
lock (LockObject)
{
return Random.Next(minValue, maxValue);
}
}
}

使い方です。

C#
int value = RandomUtil.Next(1, 101);

シングルスレッドであれば単純なstatic Randomでも十分ですが、複数スレッドからアクセスする可能性があるなら同期を入れましょう。

9. セキュリティ用途ではRandomを使ってはいけない

9-1. Randomは暗号学的に安全ではない

Randomは一般的な疑似乱数生成には便利ですが、暗号学的に安全な乱数ではありません。

次のような用途には使わないでください。

C#
// NG: セキュリティ用途にRandomを使わない
Random random = new Random();
int token = random.Next();

パスワード、認証コード、セッションID、APIキー、リセットトークンなどには、RandomNumberGeneratorを使います。

9-2. パスワード・トークン生成にRandomが不向きな理由

RandomはSeedや生成アルゴリズムに基づく疑似乱数です。攻撃者に生成パターンを推測されるリスクがあるため、セキュリティ用途には向きません。

たとえば、次のようなコードは避けるべきです。

C#
Random random = new Random();

string token = random.Next(100000, 999999).ToString();

一見ランダムに見えても、認証や秘密情報に関わる値には不十分です。

9-3. RandomNumberGeneratorを使うべきケース

RandomNumberGeneratorは、暗号学的に強いランダム値を生成するためのクラスです。GetInt32では、暗号強度の高い乱数ジェネレーターを使って、指定範囲の整数を生成できます。

C#
using System.Security.Cryptography;

int value = RandomNumberGenerator.GetInt32(1, 101);

Console.WriteLine(value);

パスワード、トークン、認証コードなど、予測されてはいけない値にはこちらを使いましょう。

9-4. 安全な乱数生成のサンプルコード

ランダムなトークン文字列を生成する例です。

C#
using System.Security.Cryptography;

byte[] bytes = RandomNumberGenerator.GetBytes(32);

string token = Convert.ToBase64String(bytes);

Console.WriteLine(token);

URLに含めたい場合は、Base64文字列の記号を調整することがあります。

C#
string token = Convert.ToBase64String(bytes)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "");

Console.WriteLine(token);

数字の認証コードを生成する例です。

C#
using System.Security.Cryptography;

int code = RandomNumberGenerator.GetInt32(100000, 1000000);

Console.WriteLine(code);

このコードでは、100000〜999999の6桁コードを生成します。

9-5. RandomとRandomNumberGeneratorの使い分け

ゲーム、抽選演出、サンプルデータ、シャッフルなどにはRandomを使います。

C#
int dice = Random.Shared.Next(1, 7);

認証、パスワード、トークン、秘密情報にはRandomNumberGeneratorを使います。

C#
using System.Security.Cryptography;

byte[] key = RandomNumberGenerator.GetBytes(32);

判断基準は「予測されると困るかどうか」です。予測されると困る値には、必ず暗号学的に安全な乱数を使いましょう。

10. 実用的なRandomの活用例

10-1. サイコロを作る

C#
int dice = Random.Shared.Next(1, 7);

Console.WriteLine($"サイコロの目: {dice}");

1〜6を出したいので、上限は7にします。

10-2. おみくじ・抽選機能を作る

C#
string[] fortunes =
{
"大吉",
"中吉",
"小吉",
"吉",
"凶"
};

string result = fortunes[Random.Shared.Next(fortunes.Length)];

Console.WriteLine(result);

確率を変えたい場合は、同じ要素を複数入れる方法もあります。

C#
string[] lots =
{
"A賞",
"B賞",
"B賞",
"C賞",
"C賞",
"C賞"
};

string result = lots[Random.Shared.Next(lots.Length)];

10-3. 配列からランダムに1件選ぶ

C#
string[] messages =
{
"こんにちは",
"お疲れさまです",
"今日も頑張りましょう"
};

string message = messages[Random.Shared.Next(messages.Length)];

Console.WriteLine(message);

配列やリストの要素を選ぶときは、Next(要素数)を使うのが基本です。

10-4. リストをランダムに並び替える

C#
static void Shuffle<T>(IList<T> list)
{
for (int i = list.Count - 1; i > 0; i--)
{
int j = Random.Shared.Next(i + 1);
(list[i], list[j]) = (list[j], list[i]);
}
}

使い方です。

C#
List<string> cards = new()
{
"A", "B", "C", "D", "E"
};

Shuffle(cards);

foreach (string card in cards)
{
Console.WriteLine(card);
}

10-5. ランダムな文字列を生成する

セキュリティ用途でないランダム文字列なら、Randomで生成できます。

C#
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

int length = 12;
char[] result = new char[length];

for (int i = 0; i < length; i++)
{
result[i] = chars[Random.Shared.Next(chars.Length)];
}

string randomText = new string(result);

Console.WriteLine(randomText);

ただし、パスワードやトークンに使う場合はRandomNumberGeneratorを使ってください。

10-6. ゲームでランダムな敵やアイテムを出す

C#
string[] enemies =
{
"Slime",
"Goblin",
"Dragon"
};

string enemy = enemies[Random.Shared.Next(enemies.Length)];

Console.WriteLine($"出現した敵: {enemy}");

レアアイテムを確率で出す例です。

C#
double rate = Random.Shared.NextDouble();

if (rate < 0.01)
{
Console.WriteLine("レアアイテムを入手しました!");
}
else
{
Console.WriteLine("通常アイテムを入手しました。");
}

11. C# Randomでよくあるエラー・疑問

11-1. Randomが見つからない場合の原因

Randomが見つからない場合は、名前空間やプロジェクト設定を確認します。

C#
using System;

通常のC#プロジェクトではSystemが暗黙的に使える場合もありますが、明示的に書くと分かりやすくなります。

C#
System.Random random = new System.Random();

11-2. Nextの最大値が出ないのはなぜか

Nextの上限は含まれないためです。

C#
int value = random.Next(1, 10);

このコードでは1〜9が出ます。10を出したい場合は、次のようにします。

C#
int value = random.Next(1, 11);

11-3. 毎回同じ結果にしたい場合はどうするか

Seedを固定します。

C#
Random random = new Random(12345);

for (int i = 0; i < 5; i++)
{
Console.WriteLine(random.Next(1, 101));
}

テストやデバッグでは便利です。

11-4. 毎回違う結果にしたい場合はどうするか

Seedを固定しないようにします。

C#
Random random = new Random();

または、.NET 6以降であればRandom.Sharedを使います。

C#
int value = Random.Shared.Next(1, 101);

11-5. 重複あり・重複なしはどう使い分けるか

重複ありは、サイコロや確率判定のように、同じ値が何度出てもよい場合に使います。

C#
int dice = Random.Shared.Next(1, 7);

重複なしは、ビンゴ、カード配布、抽選番号など、一度出た値を再利用したくない場合に使います。

C#
List<int> numbers = Enumerable.Range(1, 75).ToList();
Shuffle(numbers);

int first = numbers[0];

11-6. UnityEngine.Randomとの違い

Unityでは、C#標準のSystem.Randomとは別にUnityEngine.Randomがあります。

C#
// UnityEngine.Random
int value = UnityEngine.Random.Range(1, 10);

UnityのRandom.Rangeは、整数と小数で上限の扱いが異なる点に注意が必要です。通常のC#アプリではSystem.Random、Unityのゲーム開発では用途に応じてUnityEngine.RandomまたはSystem.Randomを使い分けます。

名前が衝突する場合は、完全修飾名で書くと明確です。

C#
System.Random random = new System.Random();
int value = random.Next(1, 10);

12. C# Randomのベストプラクティス

12-1. Randomインスタンスは基本的に使い回す

Randomは何度もnewするのではなく、基本的に使い回します。

C#
private static readonly Random Random = new Random();

.NET 6以降でシンプルに使うなら、Random.Sharedも有力です。

C#
int value = Random.Shared.Next(1, 101);

12-2. 範囲指定では上限値が含まれないことを意識する

1〜10がほしい場合は、上限に11を指定します。

C#
int value = Random.Shared.Next(1, 11);

0〜99がほしい場合は、上限に100を指定します。

C#
int value = Random.Shared.Next(100);

12-3. 重複回避には用途に応じた方法を選ぶ

少数の重複なし乱数を取りたいだけならHashSetが簡単です。

C#
HashSet<int> set = new();

while (set.Count < 5)
{
set.Add(Random.Shared.Next(1, 11));
}

範囲全体をランダムに並び替えたいなら、シャッフルが向いています。

C#
List<int> numbers = Enumerable.Range(1, 10).ToList();
Shuffle(numbers);

12-4. 再現性が必要な場合だけSeedを使う

Seedは、テストやデバッグなど再現性が必要な場合に使います。

C#
Random random = new Random(123);

本番で毎回違う結果が必要な処理では、Seed固定を避けましょう。

C#
Random random = new Random();

12-5. .NET 6以降はRandom.Sharedを検討する

.NET 6以降で、特別なSeed管理が不要な場合はRandom.Sharedが便利です。

C#
int number = Random.Shared.Next(1, 101);
double rate = Random.Shared.NextDouble();

インスタンス管理を簡単にでき、共有されたスレッドセーフなRandomを利用できます。

12-6. セキュリティ用途ではRandomNumberGeneratorを使う

セキュリティに関わる乱数では、RandomではなくRandomNumberGeneratorを使います。

C#
using System.Security.Cryptography;

byte[] bytes = RandomNumberGenerator.GetBytes(32);
string token = Convert.ToBase64String(bytes);

また、範囲指定の整数が必要な場合はGetInt32が使えます。

C#
using System.Security.Cryptography;

int code = RandomNumberGenerator.GetInt32(100000, 1000000);

まとめ

C#で乱数を生成する基本は、RandomクラスのNext()Next(maxValue)Next(minValue, maxValue)NextDouble()を使うことです。

整数の範囲指定では、上限値が含まれない点に注意しましょう。1〜10がほしい場合はrandom.Next(1, 11)、0〜99がほしい場合はrandom.Next(100)と書きます。

重複しない乱数が必要な場合は、HashSetで管理する方法や、リストをシャッフルする方法が使えます。特にカードやビンゴのように重複が許されない処理では、シャッフルして順番に取り出す方法が分かりやすく安全です。

同じ乱数列を再現したい場合は、new Random(seed)でSeedを指定します。一方で、毎回違う結果が必要な通常処理ではSeedを固定しないようにしましょう。

また、.NET 6以降ではRandom.Sharedを使うことで、手軽に共有の乱数生成器を利用できます。通常のアプリやゲーム演出ではRandomRandom.Sharedで十分ですが、パスワードやトークンなど予測されてはいけない値には、必ずRandomNumberGeneratorを使うことが重要です。