Interesting how random things sometimes come together. I was checking out Chris William's pre-beta rogue-like game Heroic Adventure! on CodePlex and noticed that although most of the game is written in VB, he had included some C# code in there. The motivation was the fact that the pseudo-random number generators that VB and the .NET platform provide (i.e. VB's Rnd function and .NET's System.Random) are not strong enough to satisfy the random number needs of his game. So he borrowed some C# code that implements the Mersenne Twister algorithm, which is a much better pseudo-random number generator.

I thought it was a shame for Chris to have to sully all his beautiful VB code with some C# (obligatory <g> for the humor impaired), so I took a look at the reference implementation of the algorithm and coded it up in VB (making sure, of course, the test output matched that of the reference implementation). For those of you with random number needs, I've attached it at the end. Please let me know if you find bugs.

Interestingly, at almost exactly the same time, the question of the strength (or lack thereof) of our pseudo-random number generator came up internally. Keep in mind that VB .NET's Rnd function was written to remain completely compatible with VB 6.0's Rnd function, which was compatible with VB 5.0's Rnd function, etc., etc., all the way back to VB 1.0's Rnd function. So that pseudo-random number generator is pretty freaking old -- 15+ years and counting! We're discussing what we might do about the situation, but since some people may depend on setting a particular seed and then getting a predictable sequence...

One caveat to keep in mind is that none of these pseudo-random number generators are strong enough to be used for cryptographic uses. For that, try System.Security.Cryptography.RNGCryptoServiceProvider.

'
' An implementation of the Mersenne Twister algorithm (MT19937), developed
' with reference to the C code written by Takuji Nishimura and Makoto Matsumoto
' (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html).
'
' This code is free to use for any pupose.
'

Option Strict On

''' 
''' A random number generator with a uniform distribution using the Mersenne 
''' Twister algorithm.
''' 
Public Class MersenneTwister
    Private Const N As Integer = 624
    Private Const M As Integer = 397
    Private Const MATRIX_A As UInteger = &H9908B0DFUI
    Private Const UPPER_MASK As UInteger = &H80000000UI
    Private Const LOWER_MASK As UInteger = &H7FFFFFFFUI

    Private mt(N - 1) As UInteger
    Private mti As Integer = N + 1

    ''' 
    ''' Create a new Mersenne Twister random number generator.
    ''' 
    Public Sub New()
        Me.New(CUInt(Date.Now.Millisecond))
    End Sub

    ''' 
    ''' Create a new Mersenne Twister random number generator with a
    ''' particular seed.
    ''' 
    ''' The seed for the generator.
    Public Sub New(ByVal seed As UInteger)
        mt(0) = seed
        For mti = 1 To N - 1
            mt(mti) = CUInt((1812433253UL * (mt(mti - 1) Xor (mt(mti - 1) >> 30)) + CUInt(mti)) And &HFFFFFFFFUL)
        Next
    End Sub

    ''' 
    ''' Create a new Mersenne Twister random number generator with a
    ''' particular initial key.
    ''' 
    ''' The initial key.
    Public Sub New(ByVal initialKey() As UInteger)
        Me.New(19650218UI)

        Dim i, j, k As Integer
        i = 1 : j = 0
        k = CInt(IIf(N > initialKey.Length, N, initialKey.Length))

        For k = k To 1 Step -1
            mt(i) = CUInt(((mt(i) Xor ((mt(i - 1) Xor (mt(i - 1) >> 30)) * 1664525UL)) + initialKey(j) + CUInt(j)) And &HFFFFFFFFUI)
            i += 1 : j += 1
            If i >= N Then mt(0) = mt(N - 1) : i = 1
            If j >= initialKey.Length Then j = 0
        Next

        For k = N - 1 To 1 Step -1
            mt(i) = CUInt(((mt(i) Xor ((mt(i - 1) Xor (mt(i - 1) >> 30)) * 1566083941UL)) - CUInt(i)) And &HFFFFFFFFUI)
            i += 1
            If i >= N Then mt(0) = mt(N - 1) : i = 1
        Next

        mt(0) = &H80000000UI
    End Sub

    ''' 
    ''' Generates a random number between 0 and System.UInt32.MaxValue.
    ''' 
    Public Function GenerateUInt32() As UInteger
        Dim y As UInteger
        Static mag01() As UInteger = {&H0UI, MATRIX_A}

        If mti >= N Then
            Dim kk As Integer

            Debug.Assert(mti <> N + 1, "Failed initialization")

            For kk = 0 To N - M - 1
                y = (mt(kk) And UPPER_MASK) Or (mt(kk + 1) And LOWER_MASK)
                mt(kk) = mt(kk + M) Xor (y >> 1) Xor mag01(CInt(y And &H1))
            Next

            For kk = kk To N - 2
                y = (mt(kk) And UPPER_MASK) Or (mt(kk + 1) And LOWER_MASK)
                mt(kk) = mt(kk + (M - N)) Xor (y >> 1) Xor mag01(CInt(y And &H1))
            Next

            y = (mt(N - 1) And UPPER_MASK) Or (mt(0) And LOWER_MASK)
            mt(N - 1) = mt(M - 1) Xor (y >> 1) Xor mag01(CInt(y And &H1))

            mti = 0
        End If

        y = mt(mti)
        mti += 1

        ' Tempering
        y = y Xor (y >> 11)
        y = y Xor ((y << 7) And &H9D2C5680UI)
        y = y Xor ((y << 15) And &HEFC60000UI)
        y = y Xor (y >> 18)

        Return y
    End Function

    ''' 
    ''' Generates a random integer between 0 and System.Int32.MaxValue.
    ''' 
    Public Function GenerateInt32() As Integer
        Return CInt(GenerateUInt32() >> 1)
    End Function

    ''' 
    ''' Generates a random integer between 0 and maxValue.
    ''' 
    ''' The maximum value. Must be greater than zero.
    Public Function GenerateInt32(ByVal maxValue As Integer) As Integer
        Return GenerateInt32(0, maxValue)
    End Function

    ''' 
    ''' Generates a random integer between minValue and maxValue.
    ''' 
    ''' The lower bound.
    ''' The upper bound.
    Public Function GenerateInt32(ByVal minValue As Integer, ByVal maxValue As Integer) As Integer
        Return CInt(Math.Floor((maxValue - minValue + 1) * GenerateDouble() + minValue))
    End Function

    ''' 
    ''' Generates a random floating point number between 0 and 1.
    ''' 
    Public Function GenerateDouble() As Double
        Return GenerateUInt32() * (1.0 / 4294967295.0)
    End Function
End Class

Updated 09/22/2006: The dingo ate my XML comments! Fixed.

Updated 09/22/2006 (Later): The dingo ate my <g> as well! Fixed.