2012-02-16 5 views
-1

複数の実行スレッドにわたってランダム浮動小数点数を素早く生成する必要があります。私はSystem.Randomを使用しようとしましたが、それは私のニーズには遅すぎ、複数のスレッドにわたって同じ数を返します。 (私はシングルスレッドで自分のアプリケーションを実行するとき、それは正常に動作します。)また、私は、生成された番号は0から100Cのための高速スレッドセーフ乱数ジェネレータ

の間にあることを確認する必要がありますここで私が今しようとしているものです:

number = random.NextDouble() * 100; 

代わりに私は何を試すべきですか?

+4

ランダムであれば常に同じ番号が与えられますが、正しいとは限りません。また、 'Random'はスレッドセーフではありません。 – dasblinkenlight

+0

真の乱数を生成することは大きな問題であることにも注意してください。http://www.random.org/randomness/ –

+3

非常に速くてスレッドセーフです。return 4; –

答えて

6
ここ

は(.NET 4.0が必要です)、それが私の感想です:

public static class RandomGenerator 
{ 
    private static object locker = new object(); 
    private static Random seedGenerator = new Random(Environment.TickCount); 

    public static double GetRandomNumber() 
    { 
     int seed; 

     lock (locker) 
     { 
      seed = seedGenerator.Next(int.MinValue, int.MaxValue); 
     } 

     var random = new Random(seed); 

     return random.NextDouble(); 
    } 
} 

とそれぞれの値が一意である千回の反復のためにそれをチェックするためのテスト:

[TestFixture] 
public class RandomGeneratorTests 
{ 
    [Test] 
    public void GetRandomNumber() 
    { 
     var collection = new BlockingCollection<double>(); 

     Parallel.ForEach(Enumerable.Range(0, 1000), i => 
     { 
      var random = RandomGenerator.GetRandomNumber(); 
      collection.Add(random); 
     }); 

     CollectionAssert.AllItemsAreUnique(collection); 
    } 
} 

重複する値が返されることは絶対に保証されませんが、テストを10000回実行してテストに合格しました。

+2

これはスレッドセーフではないそれはすべてのスレッド間で単一の 'ランダム'インスタンス - 'seedGenerator' - を共有しています。遅かれ早かれ、これはマルチスレッド環境で中断されます。 – LukeH

+0

良い点、私はseedGenerator.Next()の周りのロックを逃した。私は例を更新しました。 –

+0

'1 ..の範囲で数値を生成するとき。2回連続して値「x」を生成する確率は、ゼロではなく「1/n」でなければならない。 – Bevan

2

Randomに同じ番号が与えられている場合は、誤って使用している可能性があります。連続して多数のインスタンスを作成する(同じシードを使用して同じシーケンスを生成する)か、複数のスレッド間で1つのインスタンスを使用します(マルチスレッドの使用で安全でないため、そのインスタンスを「破棄する」)。

Randomの速度とランダム性は、単一のスレッドで実行しているとき、あなたはあなたのマルチスレッドのシナリオでは、スレッドごとに個別のインスタンスを確保するためにThreadLocal<T>でそれを包む試みることができるあなたのために十分である場合:

var number = _rng.Value.NextDouble() * 100; 

// ... 

private static int _staticSeed = Environment.TickCount; 
private static readonly ThreadLocal<Random> _rng = new ThreadLocal<Random>(() => 
    { 
     int seed = Interlocked.Increment(ref _staticSeed) & 0x7FFFFFFF; 
     return new Random(seed); 
    }); 
3

良い乱数にはwindows cryptoAPIを使用します。パフォーマンスのために、8KBのランダムデータのブロックを1回呼び出して、各番号のcryptoAPIを呼び出すのではなく、その番号を配布します。通常のランダムと比較してパフォーマンスが最後に何であるかはわかりません。しかし、無作為化ははるかに優れています(Windows CryptoAPIの詳細はインターネットを参照してください)

これはコードです。

// UNIT RandomNumberGeneratorBase 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace FastLibrary 
{ 
    public abstract class RandomNumberGeneratorBase 
{  
     private int _byteBufSize; 
     private byte[] _buf; 
     private int _idx; 
     private int _lastsize; 

     public RandomNumberGeneratorBase(int bufSize = 8092) 
    {  
      _byteBufSize = bufSize; 
      _buf = new byte[_byteBufSize]; 
      _idx = _byteBufSize; 
     } 

     protected abstract void GetNewBuf(byte[] buf); 

     private void CheckBuf(int bytesFreeNeeded = 1) 
     {  
      _idx += _lastsize; 
      _lastsize = bytesFreeNeeded; 
      if (_idx + bytesFreeNeeded < _byteBufSize) { return; } 
      GetNewBuf(_buf); 
      _idx  = 0; 
      _lastsize = 0; 
     } 

     public byte GetRandomByteStartAtZero(int belowValue) 
     {  
     return (byte)(Math.Round(((double)GetRandomByte() * (belowValue - 1))/255)); 
     }  

     public int GetRandomIntStartAtZero(int belowValue) 
     {  
      return (int)(Math.Round(((double)GetRandomUInt32() * (double)(belowValue - 1))/(double)uint.MaxValue)); 
     }  

     public byte GetRandomByte() 
    {  
      CheckBuf(); 
     return _buf[_idx]; 
    }  

     public bool GetRandomBool() 
    {  
      CheckBuf(); 
     return _buf[_idx] > 127; 
    }  

     public ulong GetRandomULong() 
    {  
      CheckBuf(sizeof(ulong)); 
     return BitConverter.ToUInt64(_buf, _idx); 
    }  

     public int GetRandomInt() 
    {  
      CheckBuf(sizeof(int)); 
     return BitConverter.ToInt32(_buf, _idx); 
    }  

     /// <summary> 
     ///  Double from 0 to 1 (might be zero, will never be 1) 
     /// </summary> 
     public double GetRandomDouble() 
    {  
      return GetRandomUInt32()/(1d + UInt32.MaxValue); 
    }  

     /// <summary> 
     ///  Float from 0 to 1 (might be zero, will never be 1) 
     /// </summary> 
     public float GetRandomFloat() 
    {  
      return GetRandomUInt32()/(1f + UInt32.MaxValue); 
    }  

     public uint GetRandomUInt32() 
    {  
      CheckBuf(sizeof(UInt32)); 
      return BitConverter.ToUInt32(_buf, _idx); 
    }  
    }  
}  

// UNIT StrongRandomNumberGenerator 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Security.Cryptography; 
using System.Text; 

namespace FastLibrary 
{ 
    public sealed class StrongRandomNumberGenerator : RandomNumberGeneratorBase 
{  
     private RNGCryptoServiceProvider _rnd; 

     public StrongRandomNumberGenerator() 
    {  
      _rnd = new RNGCryptoServiceProvider(); 
    }  

     protected override void GetNewBuf(byte[] buf) 
    {  
      _rnd.GetBytes(buf); 
    }  

    } 
}