About C#:Random仅生成一个随机数字

 2019-04-29 

Random number generator only generating one random number

有以下功能:

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

我叫它:

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

如果这步环与调试器在运行时得到不同的值(这是什么?我想)。 然而,如果放在一个断点,下面的两行代码,全行业的"Mac"阵列具有平等的价值。

这是为什么发生?


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

1
2
3
4
5
6
7
8
9
//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不保证线程安全。因此,有两种有效的方法:

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

两者都可以,但同时将多个调用方的单个实例静音只会带来麻烦。

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

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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; } }
}

然后可以使用静态随机实例和代码,例如

1
StaticRandom.Instance.Next(1, 100);


Mark的解决方案可能非常昂贵,因为它每次都需要同步。

我们可以使用线程特定的存储模式来绕过同步的需要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
    }
}

测量这两个实现,您应该看到一个显著的区别。


我的回答是:

重申正确的解决方案:

1
2
3
4
5
6
7
8
9
10
11
namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

所以你可以打电话给:

1
var i = Util.GetRandom();

贯穿始终。

如果您严格地需要一个真正的无状态静态方法来生成随机数,那么您可以依赖一个Guid

1
2
3
4
5
6
7
public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

它会慢一点,但比Random.Next更随机,至少从我的经验来看。

但不是:

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

不必要的对象创建会使它变慢,特别是在循环下。

永远不会:

1
new Random().Next();

它不仅速度慢(在循环中),而且随机性…嗯,据我说不是很好。


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

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


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

2)一个提示。假设您想要创建100个对象,并且假设每个对象都应该有自己的随机生成器(如果您在很短的时间内计算随机数的负载,这很方便)。如果要在循环中执行此操作(生成100个对象),可以这样做(以确保完全随机性):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);

干杯。


每次执行时

1
Random random = new Random (15);

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

如果你使用

1
Random random = new Random ();

如果黑客猜到了种子,并且你的算法与你的系统的安全性相关,你会得到不同的随机数序列,你的算法就被破坏了。我是你处决穆特的。在此构造函数中,种子由系统时钟指定,如果在很短的时间(毫秒)内创建了多个实例,则它们可能具有相同的种子。

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

System.Security.Cryptography.RNGCryptoServiceProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}

用途:

1
int randomNumber = Next(1,100);


像这样声明随机类变量:

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

如果每次从列表中获得不同的随机数,请使用

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

每次申报一次。


有很多解决方案,这里有一个:如果你只想要数字,删除字母,方法会收到一个随机的结果长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}