在寻找生成真正随机数的最佳尝试时,我偶然发现了这个代码示例。
在此片段中寻找意见。
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,因为这种依赖对应用程序来说是不现实的。
答案 0 :(得分:19)
好吧,使用RNGCryptoServiceProvider
会给你一个不可思议的加密强度种子,而Environment.TickCount
在理论上是可预测的。
在快速连续多次调用NextInt
方法时,另一个重要区别是显而易见的。使用RNGCryptoServiceProvider
将每次使用不同的加密强度数对Random
对象进行播种,这意味着它将继续为每个调用返回不同的随机数。使用TickCount
冒险每次都使用相同的数字播种Random
对象(如果在相同的“tick”期间多次调用该方法),这意味着它将继续返回相同的(假设是随机的) )每次通话的号码。
如果您确实需要真正的随机数,那么您根本不应该使用计算机来生成它们:您应该测量放射性衰变或类似的东西,真正无法预测。
答案 1 :(得分:6)
不要使用您的代码。您的解决方案是错误的,并产生差的随机数。我建议我的解决方案,它生成加密强大的随机数:
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);
}
}
答案 2 :(得分:5)
我问了similar question 2年前:)检查一下它是否对你有所帮助。我使用该代码生成用于支付处理的安全随机数。
答案 3 :(得分:2)
我真的不建议使用提供的示例。虽然RNGCryptoServiceProvider返回真正好的随机(或至少它应该),但对于Random来说同样如此。更多 - 不知道Random(value)是否为Next(..)重新调整的值创建了真正的双射。更多 - 不能保证Next(min,max)以真正随机的方式返回值(意味着数字命中每个值的机会相等)。
我会首先将问题推迟到0区间的数字 - 最大(不包括)。然后我将使用最接近2的幂来获得0 - (2 ^ n - 1)范围内的随机值。现在你必须做的一件事就是使用modulo来获得优先范围内的数字,如rand(0 - (2 ^ n - 1))%max,因为通过这样做你实际上增加了在较低范围内编号的机会。 / p>
示例 - max = 3,n = 2(0 - (2 ^ 2 - 1))%2,数字(0,1,2,3),模数后的对应值(0,1,2,0) 。看到我们两次击中0,这实际上是糟糕的随机。
因此,解决方案是使用加密随机获取最接近2的幂,如果值超出最大范围,重复进行(获得另一个加密随机),直到值在给定范围内。这将是更好的algorythm。
答案 4 :(得分: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
}
答案 5 :(得分:2)
我觉得这可能会更简单,并且可以用更少的代码行实现,所以我将投入更多精力-这将始终返回0到1之间的正数。
public double Random()
{
using var csp = new RNGCryptoServiceProvider();
byte[] b = new byte[8];
csp.GetBytes(b);
var lng = BitConverter.ToInt64(b, 0);
var dbl = (double)(lng < 0 ? ~lng : lng);
// Convert to a random number between 0 and 1
return dbl / long.MaxValue;
}
代替每次都创建一个新的RNGCryptoServiceProvider
,一个简单的静态字段就可以了。
要返回两个数字之间的随机正整数,这可以正常工作-尽管您需要检查min
小于max
,并且两者都为正数:
public long RandomInt64(long min = 0, long max = long.MaxValue)
{
// Check arguments
if (min >= max)
{
throw new ArgumentOutOfRangeException(nameof(min), min, "Minimium value must be less than the maximum value.");
}
if (min < 0)
{
throw new ArgumentException("Minimum value must be at least 0.", nameof(min));
}
// Get the range between the specified minimum and maximum values
var range = max - min;
// Now add a random amount of the range to the minimum value - it will never exceed maximum value
var add = Math.Round(range * Random());
return (long)(min + add);
}
答案 6 :(得分:1)
这实际上取决于生成的随机数的预期用途或要求。
Random类对于实际随机化很有用,例如随机化图像旋转器中的订单图像显示或模具卷。
另一方面,如果您需要需要更高安全性的随机数,例如生成密码或付款确认密钥,则使用诸如RNGCryptoServiceProvider之类的类或创建您自己的抽象类实现{{实现加密算法的3}}是更好的选择。
答案 7 :(得分:1)
好的,所以我来晚了一点,但我真的想要一个完整的System.Random实现,可以在同一个计时器tic中多次调用并产生不同的结果。在对不同的实现感到非常痛苦之后,我决定使用我提出的最简单的一个,它提供了一个默认构造函数,它为基本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()值并通过转换推送产生整数值时,我不知道结果是如何加密的。无论如何,这是代码,如果有人想要它:
/// <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
}