Tải bản đầy đủ - 0 (trang)
2 The C# Built-In Random Number Generator

2 The C# Built-In Random Number Generator

Tải bản đầy đủ - 0trang

Random Numbers



285



on referred to simply as random numbers, are chosen with equal probability from

a finite set of allowed int32 values. The current implementation of the Random()

class is based on Knuth’s subtractive random number generator algorithm [50] which

has an alleged period of about 232 . This period is usually considered good enough

for most practical applications but is definitely not recommended for cryptography

or other applications that require a substantial degree of security such as password

generation or encrypted telecommunications.

The random number generator provided by the .NET Framework starts from a seed

value and can be overloaded in two different ways. By default, the parameterless constructor of the Random() class uses the computer’s internal system clock to generate

its own seed value whereas the parameterized constructor can accept specific seed

values from within the allowed range of Int32 integers. Because the system clock is

continuously changing in value, using a parameterless constructor will therefore initiate a different randomized numerical sequence every time it is called up. However,

since the clock has finite resolution, using the parameterless constructor to create

different Random() objects in close succession can create random number generators that produce identical randomized numerical sequences. This problem can be

avoided by creating one Random() object to generate many random numbers over

time, instead of repeatedly creating several new Random() objects to generate one

random number at a time. However, some of these features of the random number

generator can sometimes be useful. For example, if you want to repeat the same

sequence of numbers, you can set the seed state during instantiation by passing the

same integer value to the constructor.

Once this Random() object has been created we can call the Next() method to

produce a random number. The Next() method can be overloaded in three different

ways. Without any input parameters, the Next() method returns a random number

anywhere from 0 to the maximum value of int32.Max which is 2, 147, 483, 647. The

Next(int32) method returns a nonnegative random number from 0 to the Int32

integer value specified by the single input parameter. Lastly, the Next(Int32,Int32

) method returns a random number from anywhere within the allowed Int32 integer

range specified by the two input parameters.

The Random.NextBytes() method fills the elements of a specified array of bytes

with random bytes selected anywhere from 0 to 255. Therefore, the Random() class

can also be used to populate a byte array with random bytes.

Finally, the Random.NextDouble() method is not overloaded and only returns a

double-precision floating point from the interval [0, 1]. However, by properly adjusting the call to the Random.NextDouble() method this way

a + (b - a)*rnd.NextDouble();



where rnd is an object of the Random() class, we can obtain random numbers from

anywhere within the numerical interval specified by [a, b). Some actual examples

are listed below in order to illustrate the implementation of all these ideas.



© 2010 by Taylor and Francis Group, LLC



286



Numerical Methods, Algorithms and Tools in C#



static void TestRNG()

{

int seed = 234;

int min = -55;

int max = 25;

Console.WriteLine("Testing 6 random int32 from 0 to int32.MAX");

Random randObj1 = new Random(seed);

for (int j = 0; j < 6; j++)

Console.Write("{0,11} ", randObj1.Next());

Console.WriteLine("Testing 6 random int32 from 0 to 25");

Random randObj2 = new Random(seed);

for (int j = 0; j < 6; j++)

Console.Write("{0,11} ", randObj2.Next(max));

Console.WriteLine("Testing 6 random int32 from -55 to 25");

Random randObj3 = new Random(seed);

for (int j = 0; j < 6; j++)

Console.Write("{0,11} ", randObj3.Next(min, max));

Console.WriteLine("Testing 5 random bytes: 0 to 255");

Random randObj4 = new Random();

Byte[] b = new Byte[10];

randObj4.NextBytes(b);

Console.WriteLine("The Random bytes are: ");

for (int i = 0; i < 10; i++)

{

Console.Write(i);

Console.Write(":");

Console.WriteLine(b[i]);

}

Console.WriteLine("Testing 6 random doubles from 0 to 1");

Random randObj5 = new Random(seed);

for (int j = 0; j < 6; j++)

Console.WriteLine("{0,11} ",randObj5.NextDouble());

Console.WriteLine("Testing 6 random doubles from -55 to 25");

Random randObj6 = new Random(seed);

for (int j = 0; j < 6; j++)

Console.WriteLine("{0,11}",(min+(max-min)*randObj6.NextDouble()));

}



A useful application of the random number generator class is a utility that pertains

to generating passwords that can contain random letters or characters in addition to

random numbers. Since random numbers are generated by default, we must create

a new method that will contain this functionality. The implementation of a random

password generator is given below.

static string CreateRandomPassword(int passwordLength)

{

string allowedChars =

"abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";

char[] chars = new char[passwordLength];

Random rd = new Random();



© 2010 by Taylor and Francis Group, LLC



Random Numbers



287



for (int i = 0; i < passwordLength; i++)

{

chars[i] = allowedChars[rd.Next(0,allowedChars.Length)];

}

return new string(chars);

}

static void TestRandomPassword()

{

Console.WriteLine("Testing for generation of random passwords");

for (int i = 0; i < 6; i++)

{

Console.WriteLine("Password {0}={1}",i,CreateRandomPassword(10));

Thread.Sleep(2000);

}

Console.ReadLine();

}



Microsoft also provides an encryption class, called RNGCryptoServiceProvider, for

use in developing secure applications, including allegedly secure random number

generators. However, at the time of this writing there was considerable debate over

the Internet as to the level of security that this class actually is capable of providing.

Since technology advances so rapidly, you might want to double check the current

reports on this topic when you read this to see what the current security level status

of this is really like. In any event, below are two additional examples illustrating the

use of this Microsoft encryption class.

public static void BetterRandomString()

{

// create a stronger hash code using RNGCryptoServiceProvider

byte[] random = new byte[64];

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

// populate with random bytes

rng.GetBytes(random);

// convert random bytes to string

string randomBase64 = Convert.ToBase64String(random);

// display

Console.WriteLine("Random string: {0}\r\n ",randomBase64);

}

public static string CreateRandomEncryptedPassword(int PasswordLen)

{

String allowedChars =

"abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";

Byte[] randomBytes = new Byte[PasswordLen];

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

rng.GetBytes(randomBytes);

char[] chars = new char[PasswordLen];

int allowedCharCount = allowedChars.Length;

for (int i = 0; i < PasswordLen; i++)

{

chars[i] = allowedChars[(int)randomBytes[i]%allowedCharCount];

}

return new string(chars);

}



© 2010 by Taylor and Francis Group, LLC



288



Numerical Methods, Algorithms and Tools in C#



static void TestRandomEncryptedPassword()

{

for (int i = 0; i < 6; i++)

{

Console.WriteLine("Encrypted Password {0} = {1}",

i, CreateRandomEncryptedPassword(10));

Thread.Sleep(2000);

}

Console.ReadLine();

}



By default, the random number methods within the Random() class generate numbers with replacement. This means that a particular random number may get generated repeatedly. If you do not want a set of randomly generated numbers to contain

any duplication of elements, you need to code the process of generating random

numbers accordingly. There are a number of ways to obtain a list of unique random

numbers containing no repetition. For pedagogical reasons, I thought it would be

instructive to present at least three different methods to generate random numbers

having no duplicate values.

In the first and perhaps simplest method, you can just put the values you want

randomized (numbers, strings, objects,. . .) in an array and then shuffle the array by

randomly exchanging the array elements. This procedure, using the Knuth-FisherYates shuffle algorithm, will produce a randomized list of unique numerical values

as illustrated in the example below.

static int[] UniqueRandom1(int max)

{

Random rand = new Random();

int[] array = new int[max];



// Initialize the array to integers from 0 to max

for (int i = 0; i < array.Length; i++)

{ array[i] = i; }

for (int i = array.Length - 1; i > 0; i--)

{

int randomPosition = rand.Next(i+1);

int temp = array[i];

array[i] = array[randomPosition];

array[randomPosition] = temp;

}

return array;

}

static void TestUniqueRandom1()

{

int size = 15;

int[] myData = UniqueRandom1(size);

Console.WriteLine("Testing unique random numbers version 1\n");

for (int i = 0; i < myData.Length; i++)

{ Console.WriteLine("myData[{0}] = {1}", i, myData[i]); }

}



© 2010 by Taylor and Francis Group, LLC



Random Numbers



289



As an alternate method for obtaining a list of unique random numbers you can also

start by first creating an ArrayList object to hold the numbers. Next, generate a random number within range of the desired values and add this number to the list. Then

loop repeatedly generating another random numbers and checking whether each of

them is already contained in the list. If the number is not found in the list, then add

that number to the list. Otherwise, if the number is found in the list then ignore it

and get another random number. Repeat this process until the list has been completely filled. The random numbers left in the list should all be unique without any

repetition. The code below implements this idea.

static ArrayList UniqueRandom2(int max)

{

// Create an ArrayList object that will hold the numbers

ArrayList lstNumbers = new ArrayList();

// Create an object from the Random() class that

// will be used to generate random numbers

Random rndNumber = new Random();



// Get a random number between 0 and the max requested value

int number = rndNumber.Next(0, max);

// Add this first random number to the list

lstNumbers.Add(number);

// Setup a number counter to keep track of the

// numbers being added to the list

int count = 0;

do // Repeatedly...

{

// generate a random number between 0 and the max

number = rndNumber.Next(0, max);

// If the newly generated number in not yet in the list...

if (!lstNumbers.Contains(number))

{

lstNumbers.Add(number); // ... add it

count++;

// and increase the counter

}

} while (count <= max);

// Once the list is built, return it

return lstNumbers;

}

static void TestUniqueRandom2()

{

const int Total = 15;

ArrayList lstNumbers = UniqueRandom2(Total);

for (int i = 0; i < lstNumbers.Count; i++)

Console.WriteLine("x[{0}] = {1}", i, lstNumbers[i]);

Console.ReadLine();

}



As a final, but perhaps much subtler, example of a method for obtaining a list of

unique random numbers consider the following algorithm and its subsequent implementation shown below. First, a generic list is created and loaded with the values



© 2010 by Taylor and Francis Group, LLC



290



Numerical Methods, Algorithms and Tools in C#



you want randomized (numbers, strings, objects,. . .). Individual elements from this

list are then randomly selected and added to another list. After an element has been

selected and added to the second list, it is removed from the original list so that it

cannot be selected again. This process is repeated until there are no more elements

to select from in the original list. An implementation of this algorithm in C# is given

below.

public static List GetUniqueRandomList(int size)

{

List list = new List();

Random randomGenerator = new Random();

List range = new List();

for (int i = 0; i < size; i++)

range.Add(i);

while (range.Count > 0)

{

int item = range[randomGenerator.Next(range.Count)];

list.Add(item);

range.Remove(item);

}

return list;

}

static void TestUniqueRandom3()

{

List iList = new List();

iList = GetUniqueRandomList(15);

foreach (int i in iList)

{

Console.WriteLine(i);

}

Console.ReadLine();

}



10.3 Other Random Number Generators

There are many different types of random number generators [51] but the Linear

Congruential Generator (LCG) represents one of the oldest and best-known pseudorandom number generator algorithms [50]. The linear congruential generator is

defined by the recurrence relation:

Xn+1 = (aXn + c) mod m

where Xn is the sequence of pseudorandom values, m > 0 is called the modulus, a

where 0 < a < m is called the multiplier, c where 0 ≤ c < m is called the increment,



© 2010 by Taylor and Francis Group, LLC



Random Numbers



291



and X0 where 0 ≤ X0 < m is called the seed or start value. Together these values

specify the LCG. The period of the LCG is at most m, and largely depends on the

value chosen for the other variables. The LCG will have a full period if and only if

c and m are relatively prime, a − 1 is divisible by all prime factors of m and a − 1

is a multiple of 4 if m is a multiple of 4. While capable of producing decent pseudorandom numbers, LCGs are extremely sensitive to the choice of the coefficients c,

m, and a. The most efficient linear congruent generators, including the one built into

Visual C#, reportedly have an m equal to a power of 2, most often around m = 232 .

Perhaps the most famous and controversial one of all the random number generators is the Mersenne Twister published in 1998 by Matsumoto and Nishimura [52,

53], which has a reportedly proven period of 219937 − 1. In practice, however, there is

little reason to use anything larger as most applications do not require 219937 unique

combinations (219937 ≈ 4.3 × 106001). Nevertheless, the Mersenne Twister algorithm

has received some criticism in the computer science field. Critics claim that while

the Mersenne Twister is good at generating random numbers, it is not very elegant

and is overly complex to implement. As an alternative to the Mersenne Twister, critics have proposed a simpler complementary multiply-with-carry generator [54] with

an alleged period of 1033000 which claims to be significantly faster and maintains

better or equal randomness. In any event, a C# implementation of the Mersenne

Twister random number generator algorithm is given below along with sample code

to demonstrate how to use it. Those readers wishing to learn more about how this

algorithm works are referred to the original paper [52]. Those readers looking for an

alternative to either the Visual C# or the Mersenne Twister random number generators are referred to another website [55] that maintains a very good list of random

number generators including code in C/C++ and Fortran that can be downloaded

directly to your computer.

public class MersenneTwister

{

// Class MersenneTwister generates random numbers

// from a uniform distribution using the Mersenne

// Twister algorithm.

private const int N = 624;

private const int M = 397;

private const uint MATRIX_A = 0x9908b0dfU;

private const uint UPPER_MASK = 0x80000000U;

private const uint LOWER_MASK = 0x7fffffffU;

private const int MAX_RAND_INT = 0x7fffffff;

private uint[] mag01 = {0x0U, MATRIX_A};

private uint[] mt = new uint[N];

private int

mti = N+1;

public MersenneTwister()

{ init_genrand( (uint)DateTime.Now.Millisecond); }

public MersenneTwister( int seed )

{

init_genrand( (uint)seed );

}



© 2010 by Taylor and Francis Group, LLC



292



Numerical Methods, Algorithms and Tools in C#

public MersenneTwister( int[] init )

{

uint[] initArray = new uint[init.Length];

for ( int i = 0; i < init.Length; ++i )

initArray[i] = (uint)init[i];

init_by_array( initArray, (uint)initArray.Length);

}

public static int MaxRandomInt

{ get { return 0x7fffffff; } }

public int Next()

{ return genrand_int31(); }

public int Next( int maxValue )

{ return Next( 0, maxValue ); }

public int Next( int minValue, int maxValue )

{

if ( minValue > maxValue )

{

int tmp = maxValue;

maxValue = minValue;

minValue = tmp;

}

return (int)(Math.Floor((maxValue-minValue+1)*genrand_real1()+

minValue));

}

public float NextFloat()

{ return (float) genrand_real2(); }

public float NextFloat( bool includeOne )

{

if ( includeOne )

{

return (float) genrand_real1();

}

return (float) genrand_real2();

}

public float NextFloatPositive()

{ return (float) genrand_real3(); }

public double NextDouble()

{ return genrand_real2(); }

public double NextDouble( bool includeOne )

{

if ( includeOne )

{

return genrand_real1();

}

return genrand_real2();

}



© 2010 by Taylor and Francis Group, LLC



Random Numbers



293



public double NextDoublePositive()

{ return genrand_real3(); }

public double Next53BitRes()

{ return genrand_res53(); }

public void Initialize()

{ init_genrand((uint)DateTime.Now.Millisecond); }

public void Initialize( int seed )

{ init_genrand( (uint)seed ); }

public void Initialize( int[] init )

{

uint[] initArray = new uint[init.Length];

for ( int i = 0; i < init.Length; ++i )

initArray[i] = (uint)init[i];

init_by_array( initArray, (uint)initArray.Length );

}

private void init_genrand( uint s)

{

mt[0]= s & 0xffffffffU;

for (mti=1; mti
{

mt[mti] =

(uint)(1812433253U*(mt[mti-1]ˆ(mt[mti-1]>>30))+mti);

mt[mti] &= 0xffffffffU;

}

}

private void init_by_array(uint[] init_key, uint key_length)

{

int i, j, k;

init_genrand(19650218U);

i=1; j=0;

k = (int)(N>key_length ? N : key_length);

for (; k>0; k--)

{

mt[i] = (uint)((uint)(mt[i]ˆ((mt[i-1]ˆ(mt[i-1]>>30))*1664525U

))+init_key[j]+j);

mt[i] &= 0xffffffffU;

i++; j++;

if (i>=N) { mt[0] = mt[N-1]; i=1; }

if (j>=key_length) j=0;

}

for (k=N-1; k>0; k--)

{

mt[i] = (uint)((uint)(mt[i] ˆ ((mt[i-1] ˆ (mt[i-1] >> 30)) *

1566083941U))- i);

mt[i] &= 0xffffffffU;

i++;

if (i>=N) { mt[0] = mt[N-1]; i=1; }

}

mt[0] = 0x80000000U;

}



© 2010 by Taylor and Francis Group, LLC



294



Numerical Methods, Algorithms and Tools in C#



uint genrand_int32()

{

uint y;

if (mti >= N)

{

int kk;

if (mti == N+1)

init_genrand(5489U);

for (kk=0;kk
{

y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);

mt[kk] = mt[kk+M] ˆ (y >> 1) ˆ mag01[y & 0x1U];

}

for (;kk
{

y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);

mt[kk] = mt[kk+(M-N)] ˆ (y >> 1) ˆ mag01[y & 0x1U];

}

y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);

mt[N-1] = mt[M-1] ˆ (y >> 1) ˆ mag01[y & 0x1U];

mti = 0;

}

y = mt[mti++];

y ˆ= (y >> 11);

y ˆ= (y << 7) & 0x9d2c5680U;

y ˆ= (y << 15) & 0xefc60000U;

y ˆ= (y >> 18);

return y;

}

private int genrand_int31()

{ return (int)(genrand_int32()>>1); }

double genrand_real1()

{ return genrand_int32()*(1.0/4294967295.0); }

double genrand_real2()

{ return genrand_int32()*(1.0/4294967296.0); }

double genrand_real3()

{return (((double)genrand_int32())+0.5)*(1.0/4294967296.0);}

double genrand_res53()

{

uint a=genrand_int32()>>5, b=genrand_int32()>>6;

return(a*67108864.0+b)*(1.0/9007199254740992.0);

}

}

static void TestMersenneTwister()

{

MersenneTwister randGen = new MersenneTwister();

Console.WriteLine( "100 uniform random integers in [0,{0}]:",

MersenneTwister.MaxRandomInt);

int i;



© 2010 by Taylor and Francis Group, LLC



Random Numbers



295



for (i = 0; i < 100; ++i)

{

Console.Write("{0} ",randGen.Next());

if ( i%5 == 4 ) Console.WriteLine("");

}

Console.WriteLine("100 uniform random doubles in [0,1]:");

for ( i = 0; i < 100; ++i )

{

Console.Write("{0} ",randGen.NextDouble().ToString("F8"));

if ( i%5 == 4 ) Console.WriteLine("");

}

Console.WriteLine("Press ENTER to quit");

Console.ReadLine();

}



10.4 True Random Number Generators

As we have seen, C# comes with its own built-in pseudo-random number generator

that has an alleged period of about 232 which is adequate for most practical but

non-critically secure applications. More recently, however, far more robust pseudorandom number generators have been developed. The most prominent among these

are perhaps the Mersienne Twister [52, 53] with an alleged period of 219937 − 1 and

the multiply-with-carry generator [54] with an reported period of about 1033000. With

such huge periods, these pseudo-random number generators have been very useful

in a wide range of advanced applications, such as encrypted telecommunications,

where security issues are a major concern.

True random numbers, however, can only be obtained from observing and recording naturally occurring random physical phenomena. As a result, a few websites

with links and interfaces to certain natural events have appeared on the Internet and

can therefore generate true random numbers for us. At the time of this writing, I have

compiled a list of some of these websites below and included a brief description of

the nature with which they can collect true random numbers from naturally occurring

physical events.

• Fourmilab is located in Switzerland [56] and offers free genuine random numbers by timing successive pairs of radioactive decays detected by a GeigerMăuller tube interfaced to a computer. Because of the radioactive nature of

these random numbers, they call them Hot Bits. Since the HotBits generation

hardware produces data at a modest rate of about 100 bytes per second, requests are filled from an inventory of pre-built HotBits. You order up your

serving of HotBits by filling out a request form on their webpage and specifying how many random bytes you want and in which format you would like

them delivered. Your request is relayed to the HotBits server, which flashes



© 2010 by Taylor and Francis Group, LLC



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

2 The C# Built-In Random Number Generator

Tải bản đầy đủ ngay(0 tr)

×