乱数ジェネレーターは1つの乱数のみを生成します


765

次の機能があります。

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

私の呼び方:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

ランタイム中にそのループをデバッガーでステップすると、異なる値が得られます(これが私が望んでいることです)。ただし、そのコードの2行下にブレークポイントを配置すると、mac配列のすべてのメンバーの値が等しくなります。

なぜそれが起こるのですか?


20
を使用new Random().Next((int)0xFFFF, (int)0xFFFFFF) % 256);しても、「乱数」より優れた乱数は生成されません.Next(0, 256)
。– bohdan_trotsenko

このNuGetパッケージが役立つ場合があります。これはRand.Next(int, int)、シードの再利用問題をロックしたり実行したりせずにランダム値への静的アクセスを提供する静的メソッドを提供します
ChaseMedallion

回答:


1042

実行するたびにnew Random()、時計を使用して初期化されます。つまり、タイトなループでは、同じ値が何度も得られます。単一のRandomインスタンスを保持し、同じインスタンスでNextを使用し続ける必要があります。

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

編集(コメントを参照):なぜlockここが必要なのですか?

基本的にNextは、Randomインスタンスの内部状態を変更します。複数のスレッドから同時にそれを行うと、「結果をさらにランダムにしただけだ」と主張できます実際に行っているのは内部実装を破壊する可能性があり、同じ数を取得し始める可能性もあります別のスレッドから、これ問題になるかもしれません-そうではないかもしれません。しかし、内部で何が起こるかを保証することは、より大きな問題です。スレッドの安全性は保証されてRandomいませ。したがって、2つの有効なアプローチがあります。

  • 異なるスレッドから同時にアクセスしないように同期する
  • Randomスレッドごとに異なるインスタンスを使用する

どちらでもかまいません。しかし、複数の呼び出し元からの単一のインスタンスを同時にミューテックスすることは、問題を求めているだけです。

lockこれらのアプローチの第一(及び単純な)を実現します。ただし、別のアプローチは次のとおりです。

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

これはスレッドごとなので、同期する必要はありません。


19
一般的なルールとして、すべての静的メソッドはスレッドセーフにする必要があります。複数のスレッドが同時にそれを呼び出さないことを保証することは難しいためです。通常、インスタンス(つまり、非静的)メソッドをスレッドセーフにする必要はありません
Marc Gravell

5
@Florin-2つの間に「スタックベース」の違いはありません。静的フィールドも同様に「外部状態」であり、呼び出し元間で完全に共有されます。インスタンスの場合、スレッドごとにインスタンスが異なる可能性があります(一般的なパターン)。staticを使用すると、それらすべてが共有することが保証されます([ThreadStatic]を除く)。
Marc Gravell

2
@gdoronエラーが発生していますか?「ロック」は、スレッドがここで互いにつまずくのを防ぐ必要があります...
Marc Gravell

6
オブジェクトが公開されない場合は@Dan:公開できます。(非常に理論的な)リスクは、他のスレッドが予期しない方法でロックしていることです。
Marc Gravell

3
@smironロックの外側でランダムを使用している可能性が非常に高いです。ロックしても、ロックしているものへのすべてのアクセスが妨げられるわけではありません。同じインスタンスの2つのロックステートメントが同時に実行されないようにするためです。したがってlock (syncObject)すべての random.Next()呼び出しも内にある場合にのみ役立ちますlock (syncObject)。説明したシナリオが正しいlock使用法でも発生する場合は、シングルスレッドシナリオでも発生する可能性が非常に高くなります(Random微妙に壊れているなど)。
Luaan、2015

118

アプリケーション全体での再利用を容易にするために、静的クラスが役立つ場合があります。

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

次のようなコードで静的ランダムインスタンスを使用できます

StaticRandom.Instance.Next(1, 100);

62

Markのソリューションは毎回同期する必要があるため、非常に高価になる可能性があります。

スレッド固有のストレージパターンを使用することで、同期の必要性を回避できます。


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

2つの実装を測定すると、大きな違いが見られます。


12
ロックは、競合していない場合は非常に安価です。競合している場合でも、最も興味深いシナリオでは、「番号を使って何かを実行する」コードがロックのコストを小さくすることを期待します。
Marc Gravell

4
これはロックの問題を解決しますが、これは些細な問題に対する非常に複雑な解決策ではありません。1行ではなく2行のコードを記述して乱数を生成する必要があります。これは、1行の簡単なコードの読み取りを節約するのに本当に価値がありますか?
EMP 2010

4
+1 Randomシードを取得するために追加のグローバルインスタンスを使用するのは良い考えです。ThreadLocal<T>.NET 4で導入されたクラスを使用すると、コードをさらに簡略化できることにも注意してください(Philも以下のように書いています)。
Groo、2014年

40

ここから私の答え:

適切なソリューションを繰り返すだけ:

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

だからあなたは呼び出すことができます:

var i = Util.GetRandom();

全体を通して。

乱数を生成するために真のステートレス静的メソッドが厳密に必要な場合は、に依存できますGuid

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

ほんの少し遅くなることになるだろうが、はるかにランダムでできるよりもRandom.Next、少なくとも私の経験から、。

しかし、そうではありません

new Random(Guid.NewGuid().GetHashCode()).Next();

不要なオブジェクトの作成は、特にループのもとで遅くなります。

そして決して

new Random().Next();

(ループ内で)遅いだけでなく、そのランダム性は...私によれば、あまりよくありません。


10
Guidのケースには同意しません。Randomクラスは均一分布を実装します。Guidはそうではありません。GUIDの目的は、均一に分散されていない一意であることです(そして、その実装は、ほとんどの場合、ハードウェア/マシンのプロパティに基づいており...ランダム性の反対です)。
Askolein 2013年

4
Guid生成の均一性を証明できない場合、それをランダムとして使用することは間違っています(そして、ハッシュは均一性からの別のステップになります)。同様に、衝突は問題ではありません。衝突の均一性は問題です。Guid世代がハードウェア上に存在しないことに関して、
RTFMに行き

5
「ランダム」には2つの理解があります。1。パターンの欠如または2. 確率分布(2は1に含まれる)によって記述される進化に続くパターンの欠如。あなたのGuidの例は、ケース2ではなく、ケース1で正しいです。逆:Randomクラスはケース2に一致します(したがって、ケース1も)。ケース2でない場合にのみ、の使用法を置き換えることができRandomます。ケース1はおそらく質問に答えるのに十分であり、それからあなたの作品はうまくGuid+Hashいきます。しかし、はっきりとは言われていません(ps:この制服Guid+Hash
Askolein 2013年

2
@Askoleinだけでは、いくつかのテストデータのために、私は両方のいくつかのバッチを実行Randomし、Guid.NewGuid().GetHashCode()耳鼻咽喉科(スルーfourmilab.ch/randomを)との両方が同様にランダムです。new Random(Guid.NewGuid().GetHashCode())同期した「マスター」Randomを使用して「子」Randomのシードを生成するのと同じように機能します。もちろん、それはシステムがどのようにGUIDを生成するかに依存します-私のシステムでは、それらは非常にランダムです暗号ランダムでさえあります。したがって、WindowsやMS SQLは最近は問題ないようです。ただし、モノラルやモバイルは異なる場合があります。
Luaan

2
@EdB以前にコメントで述べたように、Guid(多数)は一意であることを意図しGetHashCodeていますが、.NETのGuidはその文字列表現から派生しています。出力は私の好みのためにかなりランダムです。
nawfal

27

乱数を生成するには、次のクラスを使用します。

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);

30
私は反対投票者の1人ではありませんが、標準のPNRGは真のニーズに対応していることに注意してください。つまり、既知のシードからシーケンスを繰り返し再現できるようにすることです。時には全くのコスト真の暗号RNGのは多すぎます。また、暗号RNGが必要になる場合もあります。いわばコース用の馬。
Marc Gravell

4
ドキュメントによると、このクラスはスレッドセーフであるため、それは有利です。
ロブ教会

それを使用して、2つのランダムな文字列が1つで同じになる確率はどれくらいですか?文字列が3文字だけの場合、これは高い確率で発生すると思いますが、255文字の長さの場合、同じランダムな文字列を使用できるか、またはアルゴリズムからこれが発生しないことが保証されますか?
Lyubomir Velchev 16

16

1)マークグラベルが言ったように、1つのランダムジェネレーターを使用してみてください。これをコンストラクターSystem.Environment.TickCountに追加することは常にクールです。

2)1つのヒント。100個のオブジェクトを作成し、それぞれに独自のランダムジェネレータが必要であるとします(非常に短い期間で乱数のLOADSを計算する場合に便利です)。これをループ(100個のオブジェクトの生成)で実行する場合、次のように実行できます(完全なランダム性を保証するため)。

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

乾杯。


3
System.Environment.TickCountをループの外に移動します。繰り返し処理中にティックが発生すると、2つのアイテムが同じシードに初期化されます。別のオプションは、目盛りとiを異なる方法で組み合わせることです(例:System.Environment.TickCount << 8 + i)
Dolphin

私が正しく理解している場合、つまり、「System.Environment.TickCount + i」が同じ値になる可能性があるということですか?
サビランド09年

編集:もちろん、ループ内にTickCountを含める必要はありません。私の悪い:)。
サビランド09年

2
とにかくデフォルトのRandom()コンストラクターが呼び出しRandom(Environment.TickCount)ます
Alsty

5

実行するたび

Random random = new Random (15);

何百万回実行してもかまいません。常に同じシードを使用します。

使用する場合

Random random = new Random ();

ハッカーが種を推測し、アルゴリズムがシステムのセキュリティに関連している場合、異なる乱数シーケンスが得られます-アルゴリズムが壊れています。マルチを実行します。このコンストラクターでは、シードはシステムクロックによって指定され、いくつかのインスタンスが非常に短い時間(ミリ秒)で作成される場合、それらが同じシードを持つ可能性があります。

安全な乱数が必要な場合は、クラスを使用する必要があります

System.Security.Cryptography.RNGCryptoServiceProvider

public static int Next(int min, int max)
{
    if(min >= max)
    {
        throw new ArgumentException("Min value is greater or equals than Max value.");
    }
    byte[] intBytes = new byte[4];
    using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        rng.GetNonZeroBytes(intBytes);
    }
    return  min +  Math.Abs(BitConverter.ToInt32(intBytes, 0)) % (max - min + 1);
}

使用法:

int randomNumber = Next(1,100);

It does not matter if you execute it millions of times, you will always use the same seed. 自分でシードを指定しない限り、これは当てはまりません。
LarsTech 2018年

直した。ありがとうLarsTechと同じように、同じシードが常に指定されている場合、同じ乱数列が常に生成されます。私の答えでは、常に同じシードを使用する場合は、パラメーター付きのコンストラクターを参照します。Randomクラスは、疑似乱数のみを生成します。誰かがアルゴリズムで使用したシードを見つけた場合、アルゴリズムのセキュリティまたはランダム性が損なわれる可能性があります。RNGCryptoServiceProviderクラスを使用すると、乱数を安全に保持できます。既に訂正しました。訂正ありがとうございました。
Joma、

0

次のようにRandomクラス変数を宣言するだけです。

    Random r = new Random();
    // ... Get three random numbers.
    //     Here you'll get numbers from 5 to 9
    Console.WriteLine(r.Next(5, 10));

リストから毎回異なる乱数を取得したい場合は、

r.Next(StartPoint,EndPoint) //Here end point will not be included

毎回1回宣言することによってRandom r = new Random()


呼び出すときnew Random()はシステムクロックを使用しますが、クロックが変わる前にコード全体を2回続けて呼び出すと、同じ乱数が得られます。これが上記の回答の要点です。
サベージ

-1

解決策はたくさんありますが、ここでは1つです。数字だけを消去したい場合、メソッドはランダムに結果の長さを受け取ります。

public String GenerateRandom(Random oRandom, int iLongitudPin)
{
    String sCharacters = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
    int iLength = sCharacters.Length;
    char cCharacter;
    int iLongitudNuevaCadena = iLongitudPin; 
    String sRandomResult = "";
    for (int i = 0; i < iLongitudNuevaCadena; i++)
    {
        cCharacter = sCharacters[oRandom.Next(iLength)];
        sRandomResult += cCharacter.ToString();
    }
    return (sRandomResult);
}

基本的な問題は同じです。Randomインスタンスを渡しますが、呼び出し元が共有インスタンスを作成することを期待しています。呼び出し元が毎回新しいインスタンスを作成し、クロックが変更される前にコードが2回実行されると、同じ乱数が得られます。したがって、この回答は、誤っている可能性のある仮定を依然として行っています。
サベージ

また、乱数を生成するメソッドを持つことの要点はカプセル化です-呼び出し側のメソッドは実装について心配する必要がなく、乱数を取り戻すことにのみ関心があります
Savage
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.