C#でランダムな数値を生成する方法|Randomクラスの使い方と重複・範囲指定の注意点

はじめに

C#でランダムな数値を生成したい場合、基本的にはRandomクラスを使います。Randomクラスを使えば、整数、小数、配列からのランダム取得、抽選処理、ゲームのランダムイベントなどを簡単に実装できます。

ただし、C#のランダム処理にはいくつか注意点があります。たとえば、Random.Next(1, 100)では100は生成されません。また、new Random()を毎回作ると同じような値が出ることがあり、パスワードやトークンの生成にRandomを使うのも適切ではありません。

この記事では、C#でランダムな数値を生成する基本から、範囲指定、重複なしの乱数、Random.Shared、セキュリティ用途の乱数まで、実務でつまずきやすいポイントを含めて解説します。

1. C#でランダムな数値を生成する基本

1-1. C#のRandomクラスとは

Randomクラスは、C#で疑似乱数を生成するためのクラスです。疑似乱数とは、完全な意味でのランダムではなく、アルゴリズムによってランダムに見える数値列を生成する仕組みです。Microsoftの公式ドキュメントでも、Randomクラスは「擬似乱数ジェネレーター」と説明されています。

一般的なアプリケーションでは、サイコロ、抽選、ゲーム内のランダムイベント、テストデータ生成などにRandomを使えます。

C#
Random random = new Random();

int number = random.Next();

Console.WriteLine(number);

このコードを実行すると、0以上のランダムな整数が表示されます。

1-2. Randomクラスでできること

Randomクラスでは、主に次のようなランダム値を生成できます。

メソッド内容
Next()0以上のランダムな整数を生成
Next(maxValue)0以上、指定値未満の整数を生成
Next(minValue, maxValue)指定範囲の整数を生成
NextDouble()0.0以上1.0未満の小数を生成
NextBytes()バイト配列にランダムな値を格納

整数を作るだけならNext、小数を作るならNextDoubleを使う、と覚えておくとわかりやすいです。

1-3. まず覚えるべきNextメソッドの使い方

C#でランダムな整数を生成するときに最もよく使うのがNextメソッドです。

C#
Random random = new Random();

int value1 = random.Next(); // 0以上の整数
int value2 = random.Next(10); // 0〜9
int value3 = random.Next(1, 11); // 1〜10

Console.WriteLine(value1);
Console.WriteLine(value2);
Console.WriteLine(value3);

重要なのは、上限値は含まれないという点です。random.Next(10)なら生成される値は0〜9です。10は出ません。

1-4. int型のランダムな整数を生成する基本コード

最も基本的なコードは次のとおりです。

C#
Random random = new Random();

int number = random.Next();

Console.WriteLine(number);

Next()は、0以上int.MaxValue未満の整数を返します。公式ドキュメントでも、Next()は0以上Int32.MaxValue未満の32ビット符号付き整数を返すと説明されています。

通常は、範囲を指定せずにNext()を使うよりも、次のように用途に合った範囲を指定することが多いです。

C#
Random random = new Random();

int dice = random.Next(1, 7);

Console.WriteLine(dice); // 1〜6

2. 範囲を指定してランダムな数値を生成する方法

2-1. 0以上のランダムな整数を生成する

0以上の整数を生成するには、Next()を使います。

C#
Random random = new Random();

int value = random.Next();

Console.WriteLine(value);

ただし、この方法では上限が大きすぎるため、ゲームや抽選などではあまりそのまま使いません。実務では、0〜91〜100のように範囲を指定するケースがほとんどです。

2-2. 0以上・指定値未満の乱数を生成する

0以上、指定値未満のランダムな整数を生成するには、Next(maxValue)を使います。

C#
Random random = new Random();

int number = random.Next(10);

Console.WriteLine(number); // 0〜9

random.Next(10)は、0, 1, 2, 3, 4, 5, 6, 7, 8, 9のいずれかを返します。

配列やリストのインデックスをランダムに選ぶ場合、この形式が便利です。

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

Random random = new Random();

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

Console.WriteLine(selected);

fruits.Length3ですが、配列のインデックスは0〜2なので、random.Next(fruits.Length)と書くとちょうどよい範囲になります。

2-3. 最小値・最大値を指定して乱数を生成する

最小値と最大値を指定するには、Next(minValue, maxValue)を使います。

C#
Random random = new Random();

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

Console.WriteLine(number); // 1〜10

Next(1, 11)は、1以上11未満の整数を生成します。つまり、結果は1〜10です。

2-4. Random.Nextの最大値は含まれない点に注意

Random.Next(minValue, maxValue)では、minValueは含まれますが、maxValueは含まれません。公式ドキュメントでも、Next(Int32, Int32)minValueからmaxValue - 1までの範囲の整数を返すと説明されています。

たとえば、次のコードでは100は出ません。

C#
int number = random.Next(1, 100); // 1〜99

1〜100を生成したい場合は、上限に101を指定します。

C#
int number = random.Next(1, 101); // 1〜100

C#のランダム処理でよくあるバグの多くは、この「最大値は含まれない」という仕様を忘れることが原因です。

2-5. 1〜100など任意の範囲で乱数を作るコード例

任意の範囲でランダムな整数を作る場合は、次のように書きます。

C#
Random random = new Random();

int min = 1;
int max = 100;

int number = random.Next(min, max + 1);

Console.WriteLine(number); // 1〜100

最大値を含めたい場合は、max + 1を指定するのがポイントです。

ただし、maxint.MaxValueの場合はmax + 1でオーバーフローする可能性があります。通常の範囲指定では問題になりにくいですが、大きな数値を扱う場合は注意しましょう。

3. 小数や真偽値などさまざまなランダム値を生成する

3-1. NextDoubleで0.0以上1.0未満の小数を生成する

小数のランダム値を生成するには、NextDouble()を使います。

C#
Random random = new Random();

double value = random.NextDouble();

Console.WriteLine(value); // 0.0以上1.0未満

NextDouble()は、0.0以上1.0未満のdouble値を返します。確率判定やランダムな割合を扱うときによく使います。

3-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); // 10.0以上20.0未満

NextDouble()0.0〜1.0未満なので、範囲の幅を掛けて、最後に最小値を足します。

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

ランダムなbool値を作るには、01を生成して判定します。

C#
Random random = new Random();

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

Console.WriteLine(result); // true または false

random.Next(2)0または1を返します。そのため、0ならtrue1ならfalseのように扱えます。

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

配列からランダムに要素を取得する場合は、ランダムなインデックスを作ります。

C#
Random random = new Random();

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

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

Console.WriteLine(selected);

リストの場合も同じです。

C#
Random random = new Random();

List<string> names = new List<string>
{
"佐藤",
"鈴木",
"田中",
"高橋"
};

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

Console.WriteLine(selected);

要素数が0の配列やリストに対してrandom.Next(0)を呼ぶとエラーになるため、事前に件数を確認しましょう。

C#
if (names.Count > 0)
{
string selected = names[random.Next(names.Count)];
Console.WriteLine(selected);
}

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

ランダムな英数字の文字列を生成するには、使用する文字を用意し、ランダムに選びます。

C#
Random random = new Random();

const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
int length = 12;

char[] result = new char[length];

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

string randomString = new string(result);

Console.WriteLine(randomString);

この方法は、テスト用IDや仮のファイル名などには使えます。ただし、パスワード、APIキー、認証トークンなどのセキュリティ用途にはRandomではなくRandomNumberGeneratorを使いましょう。

4. ランダムな数値の重複を避ける方法

4-1. Random.Nextだけでは重複が発生する理由

Random.Nextは、指定した範囲から毎回ランダムに値を選びます。前回出た値を自動で覚えて除外してくれるわけではありません。

そのため、次のようなコードでは同じ値が何度も出る可能性があります。

C#
Random random = new Random();

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

1〜10の範囲で10回生成しても、必ず1〜10が1回ずつ出るわけではありません。重複なしにしたい場合は、別の工夫が必要です。

4-2. HashSetを使って重複しない乱数を生成する

重複を避けたい場合、HashSet<int>を使う方法があります。

C#
Random random = new Random();

HashSet<int> numbers = new HashSet<int>();

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

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

HashSetは同じ値を追加しても重複登録されないため、重複なしの乱数を作るのに向いています。

ただし、生成したい個数が範囲の数に近い場合、同じ値が何度も出てループ回数が増える可能性があります。

4-3. Listをシャッフルして重複なしの乱数を取得する

重複なしで効率よくランダムな値を取りたい場合は、先に候補リストを作ってシャッフルする方法がおすすめです。

C#
Random random = new Random();

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

for (int i = numbers.Count - 1; i > 0; i--)
{
int j = random.Next(i + 1);

int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}

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

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

この方法なら、1〜10の中から重複なしで5個取り出せます。

4-4. 重複なしで大量の乱数を作るときの注意点

重複なしの乱数を大量に作る場合は、次の点に注意しましょう。

状況おすすめ方法
範囲が広く、取得数が少ないHashSet
範囲が狭く、取得数が多いListをシャッフル
すべての値をランダム順に使いたいListをシャッフル
抽選番号を重複なしにしたい候補リストを作ってシャッフル

たとえば、1〜1000000から10個だけ取るならHashSetでも十分です。一方、1〜100から90個取るなら、リストをシャッフルしたほうが効率的です。

4-5. ガチャ・抽選・番号生成で使える実装例

抽選番号を重複なしで生成する例です。

C#
Random random = new Random();

List<int> tickets = Enumerable.Range(1, 100).ToList();

// シャッフル
for (int i = tickets.Count - 1; i > 0; i--)
{
int j = random.Next(i + 1);

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

// 当選番号を5つ取得
List<int> winners = tickets.Take(5).ToList();

Console.WriteLine("当選番号:");

foreach (int winner in winners)
{
Console.WriteLine(winner);
}

抽選、座席番号、順番決め、ビンゴ風の番号生成などに応用できます。

5. Randomクラスを使うときの注意点

5-1. Randomを毎回newすると同じ値が出ることがある

古い.NET Framework環境では、短時間にnew Random()を繰り返すと、同じシード値になり、同じような乱数列が生成されることがあります。公式ドキュメントでも、連続してRandomオブジェクトを作ると同じ乱数シーケンスになる可能性があり、1つのRandomオブジェクトを使うことが推奨されています。

避けたいコードは次のような書き方です。

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

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

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

基本的には、Randomのインスタンスは使い回します。

C#
Random random = new Random();

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

クラス内で使う場合は、フィールドとして保持しておくとよいです。

C#
public class Lottery
{
private readonly Random _random = new Random();

public int Draw()
{
return _random.Next(1, 101);
}
}

これにより、毎回新しい乱数生成器を作る必要がなくなります。

5-3. seed値を指定した場合の挙動

Randomには、シード値を指定できます。

C#
Random random = new Random(123);

Console.WriteLine(random.Next());
Console.WriteLine(random.Next());
Console.WriteLine(random.Next());

同じシード値を指定すると、同じ順番で乱数が生成されます。公式ドキュメントでも、同じシードを個別のRandomオブジェクトに使うと同じ一連の乱数が生成されると説明されています。

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

Console.WriteLine(random1.Next());
Console.WriteLine(random2.Next());

この2つは同じ値を出します。

5-4. 再現性のある乱数が必要なケース

シード値の指定は、次のようなケースで便利です。

ケース目的
テスト毎回同じ結果にして検証しやすくする
ゲームのリプレイ同じ乱数列で同じ展開を再現する
シミュレーション条件を固定して比較しやすくする
デバッグランダム要素による不具合を再現する

たとえば、テストでは次のように書くと、毎回同じ結果を確認できます。

C#
Random random = new Random(100);

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

Console.WriteLine(value);

5-5. マルチスレッド環境でRandomを使うときの注意点

通常のRandomインスタンスはスレッドセーフではありません。複数スレッドから同じRandomインスタンスを同時に使う場合は、lockで保護するか、後述するRandom.Sharedを使うのが安全です。公式ドキュメントでも、Randomクラスはスレッドセーフではなく、複数スレッドから使う場合は同期が必要と説明されています。

C#
private static readonly Random _random = new Random();
private static readonly object _lock = new object();

public static int GetRandomNumber()
{
lock (_lock)
{
return _random.Next(1, 101);
}
}

.NET 6以降であれば、Random.Sharedを使うとよりシンプルに書けます。

6. Random.Sharedの使い方

6-1. Random.Sharedとは

Random.Sharedは、共有されたRandomインスタンスを簡単に使えるプロパティです。公式ドキュメントでは、Random.Sharedは任意のスレッドから同時に使用できるスレッドセーフなRandomインスタンスを提供すると説明されています。

従来のようにnew Random()を用意しなくても、次のように直接使えます。

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

Console.WriteLine(number);

6-2. Random.Sharedを使ったシンプルな乱数生成

Random.Sharedを使うと、コードが短くなります。

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

Console.WriteLine(dice);

小数も生成できます。

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

Console.WriteLine(rate);

配列からランダムに取得する場合も簡単です。

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

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

Console.WriteLine(selected);

6-3. Random.Sharedとnew Randomの違い

Random.Sharednew Random()の主な違いは次のとおりです。

書き方特徴
new Random()自分でインスタンスを作る
Random.Shared共有インスタンスを使う
new Random(seed)再現性のある乱数列を作れる
Random.Sharedシードを指定できない

通常のランダム処理ならRandom.Sharedで十分です。一方、テストやシミュレーションのようにシード値を指定したい場合は、new Random(seed)を使います。

6-4. .NET 6以降でRandom.Sharedを使うメリット

.NET 6以降のプロジェクトでは、Random.Sharedを使うことで次のメリットがあります。

メリット内容
簡単new Random()を書かなくてよい
使い回しやすい共有インスタンスを使える
スレッドセーフ複数スレッドから扱いやすい
ミスを減らせる毎回new Random()する問題を避けやすい

たとえば、Webアプリや並列処理のあるアプリでは、通常のRandomインスタンスを共有するよりもRandom.Sharedを使うほうが扱いやすい場面があります。

6-5. どの書き方を選ぶべきか

基本的な使い分けは次のとおりです。

用途おすすめ
普通にランダムな数値を作りたいRandom.Shared
.NET Frameworkなど古い環境static readonly Random
テストで結果を固定したいnew Random(seed)
セキュリティ用途RandomNumberGenerator

新しい.NET環境で、特別な理由がなければRandom.Sharedを使うのがシンプルです。

7. セキュリティ用途で使う乱数の注意点

7-1. Randomクラスは暗号用途に向かない

Randomクラスは疑似乱数を生成するクラスであり、暗号用途には向きません。Microsoftの公式ドキュメントでも、ランダムなパスワードの作成に適した乱数など、暗号で保護された乱数を生成するにはRandomNumberGeneratorを使うよう説明されています。

次のような用途にRandomを使うのは避けましょう。

用途Randomを使うべきでない理由
パスワード生成推測されるリスクがある
APIキー生成セキュリティ上危険
認証トークン予測されると不正利用される
ワンタイムコード安全性が重要
暗号鍵暗号強度が必要

7-2. パスワードやトークン生成にはRandomNumberGeneratorを使う

セキュリティ用途では、System.Security.Cryptography.RandomNumberGeneratorを使います。公式ドキュメントでも、RandomNumberGeneratorは暗号的に強力なランダム値を作成すると説明されています。

C#
using System.Security.Cryptography;

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

string token = Convert.ToBase64String(bytes);

Console.WriteLine(token);

このコードでは、32バイトのランダムデータを生成し、Base64文字列に変換しています。

7-3. RandomクラスとRandomNumberGeneratorの違い

RandomRandomNumberGeneratorの違いは次のとおりです。

項目RandomRandomNumberGenerator
種類疑似乱数暗号論的乱数
速度速いやや重い
用途ゲーム、抽選、テストパスワード、トークン、鍵
シード指定可能通常は指定しない
セキュリティ用途不向き向いている

通常のランダム処理にはRandom、安全性が必要なランダム処理にはRandomNumberGeneratorを使いましょう。

7-4. セキュアな乱数が必要なケース

セキュアな乱数が必要なケースには、次のようなものがあります。

ケース
認証パスワードリセットトークン
認可APIキー、アクセストークン
暗号暗号鍵、ソルト、IV
セッション管理セッションID
セキュリティコードワンタイムパスコード

これらは推測されると重大な問題になるため、RandomではなくRandomNumberGeneratorを使います。

7-5. 安全なランダム値生成のコード例

セキュアなランダム整数を生成する場合は、RandomNumberGenerator.GetInt32が使えます。GetInt32は、暗号強度の高い乱数ジェネレーターを使って、下限を含み上限を含まない範囲の整数を生成します。

C#
using System.Security.Cryptography;

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

Console.WriteLine(number); // 1〜100

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

C#
using System.Security.Cryptography;

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

string token = Convert.ToBase64String(bytes)
.Replace("+", "")
.Replace("/", "")
.Replace("=", "");

Console.WriteLine(token);

より単純に16進文字列が必要な場合は、環境によってはGetHexStringも利用できます。

C#
using System.Security.Cryptography;

string token = RandomNumberGenerator.GetHexString(32);

Console.WriteLine(token);

8. よくあるエラー・つまずきポイント

8-1. 最大値が出ないのはなぜか

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

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

この場合、出る値は1〜9です。10は出ません。

1〜10を生成したい場合は次のようにします。

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

この仕様は、配列やリストのインデックス指定と相性がよいです。

C#
string selected = items[random.Next(items.Length)];

8-2. 乱数が毎回同じような値になる原因

主な原因は次の3つです。

原因対策
毎回new Random()しているインスタンスを使い回す
同じシードを指定しているシードを変える、または指定しない
範囲が狭い範囲を見直す

特にループ内でnew Random()する書き方は避けましょう。

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

次のように、ループの外で作ります。

C#
Random random = new Random();

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

8-3. 重複しない乱数がうまく作れない原因

Random.Nextは重複を避ける機能を持っていません。重複なしにしたい場合は、HashSetまたはシャッフルを使います。

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

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

ただし、生成したい個数が範囲より多いと無限ループになります。

C#
// 1〜10の範囲で11個の重複なし乱数は作れない

事前にチェックしましょう。

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

if (count > max - min + 1)
{
throw new ArgumentException("指定範囲より多い個数は生成できません。");
}

8-4. 範囲外エラーが発生する原因

配列やリストからランダムに取得するときは、インデックスの範囲に注意が必要です。

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

// NG: 0〜3が出る可能性があるため、3が出ると範囲外
string selected = items[Random.Shared.Next(0, items.Length + 1)];

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

C#
string selected = items[Random.Shared.Next(items.Length)];

また、空のリストでNext(0)を呼ぶとエラーになります。

C#
if (items.Length > 0)
{
string selected = items[Random.Shared.Next(items.Length)];
}

8-5. int以外の型で乱数を作りたい場合

int以外の型では、次のように変換や計算を組み合わせます。

C#
// double
double d = Random.Shared.NextDouble();

// bool
bool b = Random.Shared.Next(2) == 0;

// decimal
decimal m = (decimal)Random.Shared.NextDouble() * 100m;

// longの簡易例
long l = Random.Shared.NextInt64(1, 1000000);

NextInt64が使える環境では、long型のランダム値も生成できます。

9. C#のランダム生成でよく使う実装パターン

9-1. サイコロのように1〜6の乱数を生成する

サイコロのように1〜6を生成するには、上限に7を指定します。

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

Console.WriteLine(dice);

2個のサイコロを振る場合は次のように書けます。

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

int total = dice1 + dice2;

Console.WriteLine($"{dice1} + {dice2} = {total}");

9-2. ランダムな順番に並び替える

リストをランダムな順番に並び替えるには、シャッフルを使います。

C#
List<string> names = new List<string>
{
"佐藤",
"鈴木",
"田中",
"高橋"
};

for (int i = names.Count - 1; i > 0; i--)
{
int j = Random.Shared.Next(i + 1);
(names[i], names[j]) = (names[j], names[i]);
}

foreach (string name in names)
{
Console.WriteLine(name);
}

発表順、出席順、チーム分けなどに使えます。

9-3. ランダム抽選機能を作る

リストから1人を抽選する例です。

C#
List<string> users = new List<string>
{
"user1",
"user2",
"user3",
"user4"
};

if (users.Count == 0)
{
Console.WriteLine("抽選対象がありません。");
}
else
{
string winner = users[Random.Shared.Next(users.Count)];
Console.WriteLine($"当選者: {winner}");
}

複数人を重複なしで抽選する場合は、シャッフルしてから取得します。

C#
List<string> shuffled = users.ToList();

for (int i = shuffled.Count - 1; i > 0; i--)
{
int j = Random.Shared.Next(i + 1);
(shuffled[i], shuffled[j]) = (shuffled[j], shuffled[i]);
}

List<string> winners = shuffled.Take(2).ToList();

9-4. 確率を指定して処理を分岐する

確率で処理を分ける場合は、NextDouble()が便利です。

C#
double probability = 0.3; // 30%

if (Random.Shared.NextDouble() < probability)
{
Console.WriteLine("当たり");
}
else
{
Console.WriteLine("はずれ");
}

10%の確率なら0.1、50%なら0.5を指定します。

整数で表現する方法もあります。

C#
// 30%の確率で当たり
if (Random.Shared.Next(100) < 30)
{
Console.WriteLine("当たり");
}

9-5. ゲーム開発で使うランダム処理

ゲーム開発では、ランダム処理がよく使われます。

C#
// 敵の出現位置
int x = Random.Shared.Next(0, 100);
int y = Random.Shared.Next(0, 100);

// ダメージのばらつき
int damage = Random.Shared.Next(80, 121);

// クリティカル判定
bool isCritical = Random.Shared.NextDouble() < 0.1;

if (isCritical)
{
damage *= 2;
}

Console.WriteLine($"Damage: {damage}");

ゲームでは、完全にランダムにするだけでなく、プレイヤー体験を考えて確率や範囲を調整することが重要です。

10. C#のRandomクラスに関するFAQ

10-1. Random.Nextの引数は何を意味する?

Random.Nextの代表的な使い方は次の3つです。

C#
random.Next();        // 0以上の整数
random.Next(10); // 0〜9
random.Next(1, 11); // 1〜10

Next(maxValue)maxValueは含まれません。Next(minValue, maxValue)maxValueも含まれません。

10-2. Random.NextDoubleで範囲指定はできる?

NextDouble()自体に最小値・最大値を指定する引数はありません。

範囲指定したい場合は、計算で変換します。

C#
double min = 5.0;
double max = 10.0;

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

このコードでは、5.0以上10.0未満の小数を生成できます。

10-3. 重複しない乱数はRandomだけで作れる?

Random.Nextだけでは重複を避けられません。重複なしにしたい場合は、HashSetListのシャッフルを組み合わせます。

少数を取り出すならHashSet、範囲全体から重複なしで取り出すならシャッフルがおすすめです。

10-4. RandomとRandom.Sharedはどちらを使うべき?

.NET 6以降で、通常のランダム処理を行うならRandom.Sharedがシンプルです。

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

ただし、シード値を指定して再現性を持たせたい場合はnew Random(seed)を使います。

C#
Random random = new Random(123);

古い.NET Framework環境では、static readonly Randomとして1つのインスタンスを使い回す方法がよく使われます。

10-5. パスワード生成にRandomを使ってもよい?

パスワード生成にRandomを使うべきではありません。パスワード、トークン、APIキー、セッションIDなどには、暗号的に強力な乱数を生成できるRandomNumberGeneratorを使います。

C#
using System.Security.Cryptography;

string password = RandomNumberGenerator.GetHexString(16);

Console.WriteLine(password);

通常の抽選やゲーム処理にはRandom、セキュリティ用途にはRandomNumberGeneratorと使い分けましょう。

まとめ

C#でランダムな数値を生成するには、基本的にRandomクラスを使います。整数ならNext、小数ならNextDouble、配列やリストからのランダム取得ならNext(Count)を使うのが基本です。

特に重要なのは、Random.Nextの上限値は含まれないという点です。1〜100を生成したい場合は、random.Next(1, 101)のように書きます。

また、重複しない乱数を作りたい場合はHashSetやリストのシャッフルを使います。Randomを毎回newするのは避け、通常はインスタンスを使い回すか、.NET 6以降ならRandom.Sharedを使うとシンプルです。

一方で、パスワードやトークンなどのセキュリティ用途ではRandomを使わず、RandomNumberGeneratorを使う必要があります。

C#のランダム処理は書き方自体は簡単ですが、範囲指定、重複、シード、スレッド、セキュリティ用途の違いを理解しておくことで、実務でも安全で意図通りの実装ができます。