Three ways of C# generating random numbers

 

Random number is defined as: all numbers generated have nothing to do with each other

Random numbers are often used in practical applications, such as generating unique order numbers

There are three ways to obtain random numbers in C#:

 

I Random class

The default nonparametric constructor of Random class can carry out a series of algorithms to obtain pseudo-Random numbers within the required range according to the current system clock

Random rd = new Random();
int i = rd.Next();

This Random number can achieve some goals with low requirements, but if the system clock seed obtained by Random class is close to or even exactly the same in the case of high concurrency, it is likely to be repeated. Here is an example of loop

for (int i = 0; i < 10; i++)
{
    Random rd = new Random();  //No parameter means using the system clock as the seed
    Console.WriteLine(rd.Next().ToString());
}

This example will output 10 identical "random numbers"

Highlight the problem: because Random's algorithm for pseudo-Random numbers is fixed, the numbers calculated according to the same seed must be the same At the speed of modern computers, the cycle is almost completed in an instant, and the seeds are the same, so there will be 10 cycles to output the same Random number

Sometimes when random numbers are generated using random, they are often not random. Why?

Random number generation method can be said to be a necessary function of any programming language. Its importance goes without saying. In C #, we usually use random class to generate random numbers. In some scenarios, I find that random numbers generated by random are not reliable. In the following example, we randomly generate 5 random numbers through circulation:

for (int i = 0; i < 5; i++) 
{     
    Random random = new Random();     
    Console.WriteLine(random.Next()); 
}

The result of this code is as follows:

2140400647 2140400647 2140400647 2140400647 2140400647

From the above results, we can see that the Random number class generates five identical numbers, which is not what we expected. Why? In order to clarify this problem, the open source Random class of Microsoft is analyzed in zero degree. It is found that the algorithm used to generate Random numbers in C # is linear congruence method. It is known from encyclopedia that this algorithm generates not absolute Random numbers, but pseudo-Random numbers. The formula of linear congruence method algorithm is:

Number N+1 = (number N * a + b)% m

In the above formula, A, B and M are constants respectively, which are the factors for generating random numbers. If A random number has never been generated through the same random object before (that is, the Next method has been called), the nth random number will be specified as A default constant. This constant is specified by default when creating A random class. Random also provides A constructor to allow developers to use their own random number factors, All this can be seen through Microsoft's official open source code:

public Random() : this(Environment.TickCount) { }  public Random(int Seed) { }

When creating a Random class through the default constructor, an environment The tickcount object is passed as a factor by default to the second constructor, environment Tickcount indicates the number of milliseconds after the operating system is started. The computing speed of the computer is much faster than that of milliseconds, which leads to a factor with millisecond precision participating in the generation process of Random numbers. However, in five cycles, we use the same factor of millisecond level to generate the same Random number. In addition, the generation of the N+1 number is directly related to the N number.

In the above example, it is assumed that the number of milliseconds since the system is started is 888 milliseconds, and it takes only 0.1 milliseconds to execute five cycles, which leads to the same 888 factor being used for the five Random objects created in the cycle, and the same nth number (constant by default) being used for the Random objects created each time. Through this assumption, it is not difficult to see that the above result is inevitable.

N ow let's change this pattern, create a Random object outside the loop, reference it in each loop, generate a Random number through it, and call the Next method on the same object many times, so as to constantly change the nth number. The code is as follows:

Random random = new Random();  
for (int i = 0; i < 5; i++) 
{     
    Console.WriteLine(random.Next()); 
}

//The results after execution are as follows:

391098894 1791722821 1488616582 1970032058 201874423

We see that this result indeed confirms our above inference that the nth number in the formula is the default constant at the first cycle; In the second cycle, the nth number is 391098894, and then the changing nth number participates in the calculation as a factor, which ensures the randomness of the result.

Although our random number looks very random, it must be a pseudo-random number. When the nth number and factor are the same, the generated random number is still a duplicate random number. Random provides a constructor with parameters that allows us to pass in a factor. If the incoming factor is random, the generated random number will be more reliable. In order to provide a factor of a reliable point, We usually use GUID to generate fill factor, which is also tested in the loop:

for (int i = 0; i < 5; i++) 
{     
    byte[] buffer = Guid.NewGuid().ToByteArray();     
    int iSeed = BitConverter.ToInt32(buffer, 0);     
    Random random = new Random(iSeed);     
    Console.WriteLine(random.Next()); 
}

//This way ensures the randomness of the filling factor, so the generated random number is also relatively reliable. The operation results are as follows:

734397360 1712793171 1984332878 819811856 1015979983

In some scenarios, such random numbers are not reliable. In order to generate more reliable random numbers, Microsoft Security. A class named RNGCryptoServiceProvider is provided under the cryptography namespace. It uses the current hardware information, process information, thread information, system startup time and current accurate time of the system as the filling factor to generate high-quality random numbers through better algorithms. Its use method is as follows:

byte[] randomBytes = new byte[4]; 
RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider(); rngServiceProvider.GetBytes(randomBytes); 
Int32 result = BitConverter.ToInt32(randomBytes, 0);

The Random number generated by this algorithm has not been found to be repeated after thousands of tests, and the quality is indeed much higher than Random. In addition, the windows api also provides an unmanaged Random number generation function CryptGenRandom. The principle of CryptGenRandom is similar to that of RNGCryptoServiceProvider, which is written in C + + NET, which needs simple encapsulation. Its prototype is as follows:

BOOL WINAPI CryptGenRandom(   
    _In_     HCRYPTPROV hProv,   
    _In_     DWORD dwLen,   
    _Inout_  BYTE *pbBuffer );

The above is the random number generation method and basic principle. You can choose the best way through requirements and scenarios. Random algorithm is simple and has high performance. It is suitable for situations with low randomness requirements. Since RNGCryptoServiceProvider needs to query several system factors mentioned above during generation, its performance is slightly weaker than random class, but random number has high quality and better reliability.

II Guid class

System.Guid

GUID (Globally Unique Identifier)

The calculation of GUID uses many numbers that can be obtained locally, such as hardware ID code, current time, etc The calculated 128 bit integer (16 bytes) can be close to the unique output  

Console.WriteLine(Guid.NewGuid().ToString());

The calculation result is a hexadecimal digit of XXXXXXXX XXXX XXXX XXXX structure Of course, this format can also be changed

 

III RNGCryptoServiceProvider class

System.Security.Cryptography.RNGCryptoServiceProvider 

RNGCryptoServiceProvider uses the implementation provided by the cryptographic service provider (CSP) to implement the cryptographic random number generator (RNG)

RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
byte[] byteCsp = new byte[10];
csp.GetBytes(byteCsp);
Console.WriteLine(BitConverter.ToString(byteCsp));

Because this class uses more rigorous algorithms Therefore, even if the following is placed in the loop, the calculated random number is different

for (int i = 0; i < 10; i++)
{
    RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();
    byte[] byteCsp = new byte[10];
    csp.GetBytes(byteCsp);
    Console.WriteLine(BitConverter.ToString(byteCsp));
}

However, the calculation of RNGCryptoServiceProvider is cumbersome, and it will consume a lot of system resources when used in the cycle, which should be paid attention to

Membership.GeneratePassword()

Membership is a convenient and fast class for role permission management. I found a very interesting method by chance. I haven't studied how to implement it

public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters);
//
// Summary:
//     Generates a random password of the specified length.
//
// Parameters:
//   numberOfNonAlphanumericCharacters:
//     The number of punctuation characters in the generated password.
//
//   length:
//     The number of characters of the generated password. The length must be between 1 and 128 characters.
//
// Return result:
//     A random password of the specified length.

for example

for (int i = 0; i < 10; i++)
{
    Response.Write(Membership.GeneratePassword(20, 1) + "<br>");
}

The result is

C!&^HoTNv3!ZHkK9BAbu

azLgER)JJ-UW8q*14yz*

I3qnb]Zxu16ht!kKZ!Q*

9U:MAQ&c1x)^aed@xe**

oL(%4JvfbP&t5*Hpl4l-

6@zj$CnhW&D+|xOf:qIk

A/!Di&l*tY$QaMH0gyzY

z^wu6{1BMq7D^+WU]>f$

1OgIJS3&09fw0F9.|aXA

8F+Gy+L{O6x{SfugME*%

 

Tags: WPF

Posted by seanko on Sat, 07 May 2022 15:17:56 +0300