真に乱数を生成するための最善の試みを探しているときに、このコード例に遭遇しました。
このスニペットに関する意見を探しています。
using System;
using System.Security.Cryptography;
private static int NextInt(int min, int max)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buffer = new byte[4];
rng.GetBytes(buffer);
int result = BitConverter.ToInt32(buffer, 0);
return new Random(result).Next(min, max);
}
ソース: http://www.vcskicks.com/code-snippet/rng-int.php
これは、次のようなティックカウントシードを使用するよりも優先されますか?
Random Rand = new Random(Environment.TickCount);
Rand.Next(min, max);
注:
Random.org などのサードパーティのランダムデータプロバイダーを探していません。このような依存関係はアプリケーションにとって現実的ではないためです。
まあ、RNGCryptoServiceProvider
を使用すると、推測できない暗号強度のシードが得られますが、Environment.TickCount
は、理論的には予測可能です。
NextInt
メソッドを続けて数回呼び出すと、もう1つの重要な違いが明らかになります。 RNGCryptoServiceProvider
を使用すると、Random
オブジェクトに毎回異なる暗号強度番号がシードされます。つまり、コールごとに異なる乱数が返されます。 TickCount
を使用すると、Random
オブジェクトに毎回同じ番号がシードされるリスクがあります(同じ「tick」中にメソッドが複数回呼び出された場合)。つまり、同じ(おそらくランダム)各呼び出しの番号。
本当に真の乱数が必要な場合は、コンピューターを使用して乱数を生成するべきではありません。放射性崩壊または同様に、本当に予測できないものを測定する必要があります。
コードを使用しないでください。あなたの解決策は間違っており、貧弱な乱数を生成します。暗号的に強力な乱数を生成する私の解決策を提案します。
public class SecureRandom : RandomNumberGenerator
{
private readonly RandomNumberGenerator rng = new RNGCryptoServiceProvider();
public int Next()
{
var data = new byte[sizeof(int)];
rng.GetBytes(data);
return BitConverter.ToInt32(data, 0) & (int.MaxValue - 1);
}
public int Next(int maxValue)
{
return Next(0, maxValue);
}
public int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException();
}
return (int)Math.Floor((minValue + ((double)maxValue - minValue) * NextDouble()));
}
public double NextDouble()
{
var data = new byte[sizeof(uint)];
rng.GetBytes(data);
var randUint = BitConverter.ToUInt32(data, 0);
return randUint / (uint.MaxValue + 1.0);
}
public override void GetBytes(byte[] data)
{
rng.GetBytes(data);
}
public override void GetNonZeroBytes(byte[] data)
{
rng.GetNonZeroBytes(data);
}
}
私は提供された例を使用することを本当に勧めません。 RNGCryptoServiceProvider
は本当に良いランダムを返します(または少なくともそうするべきです)が、同じことはRandom
には当てはまりません。さらに、Random(value)
がNext(...)
から返された値に対して真の全単射を作成するかどうかは不明です。さらに、Next(min, max)
が真にランダムな方法で値を返すことは保証されません(数値が各値にヒットする可能性が等しいことを意味します)。
最初に問題を解体して、0-max(排他的)の間隔で数値を取得します。次に、最も近い2の累乗を使用して、0-(2 ^ n-1)の範囲のランダムな値を取得します。ここで絶対にしてはいけないことの1つは、moduloを使用してRand(0 - (2^n - 1)) % max
のように優先範囲の数値を取得することです。これにより、実際に低い範囲の数値を取得する可能性が高くなります。
例:max = 3, n = 2 (0 - (2^2 - 1)) % 2
、数値(0、1、2、3)、モジュロ後の対応する値(0、1、2、0)。 0を2回ヒットすることを確認してください。これは非常にランダム性が悪いことです。
したがって、解決策は、クリプトランダムを使用して値を2の累乗に取得することです。値が最大範囲外の場合は、値が指定された範囲内になるまで手順を繰り返します(別のクリプトランダムを取得)。これははるかに優れたアルゴリズムです。
これは、上記のものよりも効率的で、おそらくより高速なジェネレータだと思います。
public static class SecureRandom
{
#region Constants
private const int INT_SIZE = 4;
private const int INT64_SIZE = 8;
#endregion
#region Fields
private static RandomNumberGenerator _Random;
#endregion
#region Constructor
static SecureRandom()
{
_Random = new RNGCryptoServiceProvider();
}
#endregion
#region Random Int32
/// <summary>
/// Get the next random integer
/// </summary>
/// <returns>Random [Int32]</returns>
public static Int32 Next()
{
byte[] data = new byte[INT_SIZE];
Int32[] result = new Int32[1];
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);
return result[0];
}
/// <summary>
/// Get the next random integer to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [Int32]</returns>
public static Int32 Next(Int32 MaxValue)
{
Int32 result = 0;
do
{
result = Next();
} while (result > MaxValue);
return result;
}
#endregion
#region Random UInt32
/// <summary>
/// Get the next random unsigned integer
/// </summary>
/// <returns>Random [UInt32]</returns>
public static UInt32 NextUInt()
{
byte[] data = new byte[INT_SIZE];
Int32[] result = new Int32[1];
do
{
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);
} while (result[0] < 0);
return (UInt32)result[0];
}
/// <summary>
/// Get the next random unsigned integer to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt32]</returns>
public static UInt32 NextUInt(UInt32 MaxValue)
{
UInt32 result = 0;
do
{
result = NextUInt();
} while (result > MaxValue);
return result;
}
#endregion
#region Random Int64
/// <summary>
/// Get the next random integer
/// </summary>
/// <returns>Random [Int32]</returns>
public static Int64 NextLong()
{
byte[] data = new byte[INT64_SIZE];
Int64[] result = new Int64[1];
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);
return result[0];
}
/// <summary>
/// Get the next random unsigned long to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt64]</returns>
public static Int64 NextLong(Int64 MaxValue)
{
Int64 result = 0;
do
{
result = NextLong();
} while (result > MaxValue);
return result;
}
#endregion
#region Random UInt32
/// <summary>
/// Get the next random unsigned long
/// </summary>
/// <returns>Random [UInt64]</returns>
public static UInt64 NextULong()
{
byte[] data = new byte[INT64_SIZE];
Int64[] result = new Int64[1];
do
{
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);
} while (result[0] < 0);
return (UInt64)result[0];
}
/// <summary>
/// Get the next random unsigned long to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt64]</returns>
public static UInt64 NextULong(UInt64 MaxValue)
{
UInt64 result = 0;
do
{
result = NextULong();
} while (result > MaxValue);
return result;
}
#endregion
#region Random Bytes
/// <summary>
/// Get random bytes
/// </summary>
/// <param name="data">Random [byte array]</param>
public static byte[] NextBytes(long Size)
{
byte[] data = new byte[Size];
_Random.GetBytes(data);
return data;
}
#endregion
}
生成される乱数の使用目的や要件によって異なります。
Random クラスは、画像回転子またはサイコロのロールに表示される画像の順序をランダム化するなどの実用的なランダム化に役立ちます。
一方、パスワードや支払い確認キーの生成など、より高度なセキュリティを必要とする乱数が必要な場合は、 RNGCryptoServiceProvider などのクラスを使用するか、抽象クラスの独自の実装 RandomNumberGenerator 暗号化アルゴリズムを実装する方が優れた選択肢です。
さて、私はパーティーに少し遅れましたが、私は本当に、同じタイマーticの間に複数回呼び出して異なる結果を生成できるSystem.Randomの完全な実装を望んでいました。さまざまな実装について苦労した後、私は思いついた最も単純な実装に落ち着きました。これは、基本System.Randomコンストラクターにランダムキーを提供するデフォルトコンストラクターを提供します。
/// <summary> An implementation of System.Random whose default constructor uses a random seed value rather than the system time. </summary>
public class RandomEx : Random
{
/// <summary> Initializes a new CryptoRandom instance using a random seed value. </summary>
public RandomEx()
: base(_GetSeed())
{ }
/// <summary> Initializes a new CryptoRandom instance using the specified seed value. </summary>
/// <param name="seed"> The seed value. </param>
public RandomEx(int seed)
: base(seed)
{ }
// The static (shared by all callers!) RandomNumberGenerator instance
private static RandomNumberGenerator _rng = null;
/// <summary> Static method that returns a random integer. </summary>
private static int _GetSeed()
{
var seed = new byte[sizeof(int)];
lock (typeof(RandomEx)) {
// Initialize the RandomNumberGenerator instance if necessary
if (_rng == null) _rng = new RNGCryptoServiceProvider();
// Get the random bytes
_rng.GetBytes(seed);
}
// Convert the bytes to an int
return BitConverter.ToInt32(seed, 0);
}
}
途中で、ランダム値のすべてを提供するためにRNGCryptoServiceProviderを使用するために必要なメソッドをオーバーライドする実装も(System.Randomクラスに組み込まれた乱数ジェネレーターに依存するのではなく)作成してテストしました。しかし、ランダムなSample()値を取得し、変換を介してPush 'emを実行して整数値を生成するまでに、結果がどれほど暗号学的に強力かはわかりません。とにかく、誰かがそれを望むなら、ここにコードがあります:
/// <summary> An implementation of System.Random that uses RNGCryptoServiceProvider to provide random values. </summary>
public class CryptoRandom : Random, IDisposable
{
// Class data
RandomNumberGenerator _csp = new RNGCryptoServiceProvider();
/// <summary> Returns a random number between 0.0 (inclusive) and 1.0 (exclusive). </summary>
protected override double Sample()
{
// Get a nonnegative random Int64
byte[] bytes = new byte[sizeof(long)];
_csp.GetBytes(bytes);
long value = BitConverter.ToInt64(bytes, 0) & long.MaxValue;
// Scale it to 0->1
return (double)value / (((double)Int64.MaxValue) + 1025.0d);
}
/// <summary> Fills the elements of the specified array of bytes with random numbers. </summary>
/// <param name="buffer"> An array of bytes to contain random numbers. </param>
public override void NextBytes(byte[] buffer)
{
_csp.GetBytes(buffer);
}
/// <summary> Returns a nonnegative random integer. </summary>
/// <returns> A 32-bit signed integer greater than or equal to zero. </returns>
public override int Next()
{
byte[] data = new byte[4];
_csp.GetBytes(data);
data[3] &= 0x7f;
return BitConverter.ToInt32(data, 0);
}
/// <summary> Returns a random integer that is within a specified range. </summary>
/// <param name="minValue"> The inclusive lower bound of the random number returned. </param>
/// <param name="maxValue"> The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. </param>
/// <returns> A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned. </returns>
public override int Next(int minValue, int maxValue)
{
// Special case
if (minValue == maxValue) return minValue;
double sample = Sample();
double range = (double)maxValue - (double)minValue;
return (int)((sample * (double)range) + (double)minValue);
}
#region IDisposible implementation
/// <summary> Disposes the CryptoRandom instance and all of its allocated resources. </summary>
public void Dispose()
{
// Do the actual work
Dispose(true);
// This object will be cleaned up by the Dispose method. Call GC.SupressFinalize to
// take this object off the finalization queue and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios:
//
// If disposing is true, the method has been called directly or indirectly by a user's code and both
// managed and unmanaged resources can be disposed.
//
// If disposing is false, the method has been called by the runtime from inside the finalizer.
// In this case, only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
if (disposing) {
// The method has been called directly or indirectly by a user's code; dispose managed resources (if any)
if (_csp != null) {
_csp.Dispose();
_csp = null;
}
// Dispose unmanaged resources (if any)
}
}
#endregion
}