随机数生成器只生成一个随机数

我有以下功能:

//Function to get random numberpublic 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);

如果我在运行时使用调试器执行该循环,我将获得不同的值(这就是我想要的)。但是,如果我在该代码下方两行放置断点,则mac数组的所有成员都具有相同的值。

为什么会这样?

238007 次浏览

每次执行new Random()时,都会使用时钟进行初始化。这意味着在紧密循环中,您会多次获得相同的值。您应该保留一个随机实例,并在相同实例上继续使用下一个

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

编辑(见评论):为什么这里需要lock

基本上,Next将改变Random实例的内部状态。如果我们从多个线程同时这样做,你会说“我们只是让结果更加随机”,但我们实际上所做的可能会破坏内部实现,我们也可能开始从不同的线程获得相同的数字,这可能是一个问题——而且可能不会。不过,内部发生的事情的保证是更大的问题;因为Random没有没有对线程安全做出任何保证。因此有两种有效的方法:

  • 同步,这样我们就不会同时从不同的线程访问它
  • 每个线程使用不同的Random实例

两者都可以;但是同时从多个调用者互斥一个单一实例只是自找麻烦。

lock实现了这些方法中的第一个(也是更简单的);然而,另一种方法可能是:

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

这是每个线程的,所以你不需要同步。

我宁愿使用以下类来生成随机数:

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

1)正如Marc Gravell所说,尝试使用一个随机生成器。将其添加到构造函数中总是很酷:System.环境. TickCount。

2)一个提示。假设你想创建100个对象,并假设它们中的每个都应该有自己的随机生成器(如果你在很短的时间内计算随机数的负载,这很方便)。如果你想在循环中这样做(生成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);

干杯。

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);}}

测量这两个实现,您应该会看到显着差异。

为了便于在整个应用程序中重复使用,静态类可能会有所帮助。

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);

我的答案这里

重复一下正确的解决方案

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();

它不仅速度较慢(在循环内),它的随机性……根据我的说法并不是很好。

有很多解决方案,这里有一个:如果你只想要数字擦除字母,该方法接收随机和结果长度。

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 random = new Random (15);

如果你执行它数百万次并不重要,你将永远使用相同的种子。

如果您使用

Random random = new Random ();

你得到不同的随机数序列,如果黑客猜测种子,而你的算法与系统的安全性有关-你的算法被破坏了。我执行mult。在这个构造函数中,种子由系统时钟指定,如果在很短的时间内(毫秒)创建了多个实例,它们可能具有相同的种子。

如果你需要安全的随机数,你必须使用类

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);

我使用Rnd()函数解决了这个问题:

Function RollD6() As UIntegerRollD6 = (Math.Floor(6 * Rnd())) + 1Return RollD6End Function

当表单加载时,我使用R随机化()方法来确保我不总是从运行到运行获得相同的随机数序列。

总是得到一个正随机数。

 var nexnumber = Guid.NewGuid().GetHashCode();if (nexnumber < 0){nexnumber *= -1;}

在Visual Basic中,这是有效的(可能可以翻译成C#,如果不是DLL引用可以是一个解决方案):

Private Function GetRandomInt(ByVal Min As Integer, ByVal Max As Integer) As IntegerStatic Generator As System.Random = New System.Random()Return Generator.Next(Min, Max)End Function

我用这个:

int randomNumber = int.Parse(Guid.NewGuid().ToString().FirstOrDefault(Char.IsDigit).ToString().Replace("\0", "0"));

性能:在我的电脑上生成100万随机数:711毫秒。

如果Guid不包含任何数字(我不知道这是否可能),则将使用0作为结果。

你可以这样使用代码:

public static class ThreadSafeRandom{private static readonly Random _global = new Random();private static readonly ThreadLocal<Random> _local = new ThreadLocal<Random>(() =>{int seed;lock (_global){seed = _global.Next();}return new Random(seed);});
public static Random Instance => _local.Value;}

此代码可以按原样使用或通过NuGet包线程安全随机发生器使用。

从. NET 6开始,Random类现在配备了一个名为Shared的静态属性:

提供可以从任何线程并发使用的线程安全Random实例。

你可以像这样使用它:

// Function to get random numberpublic static int RandomNumber(int min, int max){return Random.Shared.Next(min, max);}

访问线程安全对象的开销很小,因此如果您计划尽可能快地在单个线程上生成数百万个随机数,最好创建一个专用的Random实例,而不是依赖Shared

为什么会这样?

如前所述,每次调用new Random()时,都会得到使用相同时钟初始化的Random类的新副本(因此它返回相同的值)。

现在,从. NET 6开始,有一个易于使用和线程安全的替代方案:Random.Shared

在您的示例中,您可以删除所有函数RandomNumber,然后使用以下代码(具有相同的逻辑,但现在它工作正常):

byte[] mac = new byte[6];for (int x = 0; x < 6; ++x)mac[x] = (byte)(Random.Shared.Next(0, 255));