GENERATING RANDOM NUMBERS

Random number generators are widely used in modelling. Unfortunately, .NET framework only have class Random - to generate uniform random values. Developing my own framework, I implemented few important random numbers generators: normal (Gaussian), Bernoulli and multivalue discrete random value.

   
'
'
'  Random.vb - random number generators
'
'  Project: Framework
'  Author: S.Zabinskis
'  July, 2008
'
'

Namespace Mathematics


    Namespace Probability


        Public Class TBernoulliGenerator(Of T)

#Region "Static Variables"
            Protected Shared m_randomID As Int32 = (DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second) * 1000 + DateTime.Now.Millisecond
#End Region

#Region "Local Variables"
            Protected m_random As Random = Nothing
            Private m_p As Double
            Private m_value1 As T
            Private m_value2 As T
#End Region
            Public Sub New(ByVal p As Double)
                m_p = p
                m_random = New Random(m_randomID)
                NextID()
            End Sub

            Public Property Value1() As T
                Get
                    Return m_value1
                End Get
                Set(ByVal value As T)
                    m_value1 = value
                End Set
            End Property

            Public Property Value2() As T
                Get
                    Return m_value2
                End Get
                Set(ByVal value As T)
                    m_value2 = value
                End Set
            End Property


            Public Function NextValue() As T
                If m_random.NextDouble() < m_p Then
                    Return Value1
                Else
                    Return Value2
                End If
            End Function

            Protected Shared Sub NextID()
                m_randomID += 1
            End Sub

        End Class


        Public Class TDiscreteGenerator(Of T)


#Region "Static Variables"
            Protected Shared m_randomID As Int32 = (DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second) * 1000 + DateTime.Now.Millisecond
#End Region

#Region "Local Variables"
            Private m_random As Random = Nothing
            Private m_data As New List(Of Base.Common.Pair(Of Double, T))
            Protected m_cumulative As New List(Of Double)

            Protected m_values As New List(Of T)
            Protected m_pmap As New Dictionary(Of Double, T)
            'Protected m_a As Double = 0
            'Protected m_b As Double = 1
#End Region

            Public Sub New()
                m_random = New Random(m_randomID)
                NextID()
            End Sub

            Private Function ContainsValue(ByVal v As T) As Boolean
                For Each p As Base.Common.Pair(Of Double, T) In m_data
                    If p.Second.Equals(v) Then
                        Return True
                    End If
                Next
                Return False
            End Function


            Public Sub Add(ByVal p As Double, ByVal value As T)
                If ContainsValue(value) Then
                    Throw New Exception("Duplicate value")
                End If
                If m_data.Count = 0 Then
                    If p > 1 Then
                        Throw New Exception("Invalid probability value")
                    End If
                    m_data.Add(New Base.Common.Pair(Of Double, T)(p, value))
                Else
                    If m_data(m_data.Count - 1).First + p > 1 Then
                        Throw New Exception("Invalid probability value")
                    End If
                    m_data.Add(New Base.Common.Pair(Of Double, T)(m_data(m_data.Count - 1).First + p, value))
                End If
            End Sub


            Public Sub AddLast(ByVal value As T)
                If m_data.Count > 0 Then
                    If m_data(m_data.Count - 1).First >= 1 Then
                        Throw New Exception("Invalid probability value")
                    End If
                End If
                m_data.Add(New Base.Common.Pair(Of Double, T)(1.0, value))
            End Sub


            Public Function NextValue() As T
                Dim v As Double = m_random.NextDouble()
                For j As Int32 = 0 To m_data.Count - 1
                    If v <= m_data(j).First Then
                        Return m_data(j).Second
                    End If
                Next
                Throw New Exception("Invalid random value")
            End Function

            Protected Shared Sub NextID()
                m_randomID += 1
            End Sub

        End Class


        Public Class TUniformGenerator

#Region "Static Variables"
            Protected Shared m_randomID As Int32 = (DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second) * 1000 + DateTime.Now.Millisecond
#End Region

#Region "Local Variables"
            Protected m_random As Random = Nothing
            Protected m_a As Double = 0
            Protected m_b As Double = 1
#End Region

            Public Sub New()
                m_random = New Random(m_randomID)
                NextID()
            End Sub

            Public Sub New(ByVal a As Double, ByVal b As Double)
                m_random = New Random(m_randomID)
                NextID()
                m_a = a
                m_b = b
            End Sub

            Public Function NextValue() As Double
                Return m_a + (m_b - m_a) * m_random.NextDouble()
            End Function

            Protected Shared Sub NextID()
                m_randomID += 1
            End Sub
        End Class



        Public Class TNormalGenerator

#Region "Static Variables"
            Protected Shared m_randomID As Int32 = (DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second) * 1000 + DateTime.Now.Millisecond
#End Region

#Region "Local Variables"
            Protected m_random As Random = Nothing
            Protected m_mean As Double = 0
            Protected m_std As Double = 1
#End Region

            Public Sub New()
                m_random = New Random(m_randomID)
                NextID()
            End Sub

            Public Sub New(ByVal mean As Double, ByVal stddev As Double)
                m_random = New Random(m_randomID)
                NextID()
                m_mean = mean
                m_std = stddev
            End Sub

            Public Function NextValue() As Double
                Dim noise As Double = Math.Sqrt(-2.0 * m_std * m_std * Math.Log(1.0 - m_random.NextDouble()))
                Dim theta As Double = (m_random.NextDouble() * 2.0 * Math.PI - Math.PI)
                Return m_mean + noise * Math.Cos(theta)
            End Function

            Protected Shared Sub NextID()
                m_randomID += 1
            End Sub

        End Class


    End Namespace


End Namespace


Every random generator class has NextValue function that returns next generated number. Bernoulli and multivalue discrete random value are generics, so you can generate, for example, string random values.

Sample program to generate Bernoulli and multivalue discrete random values:

 
Module Module1

    Private Sub TestBernoulli(ByVal N As Int32)
        Dim r As New Base.Mathematics.Probability.TBernoulliGenerator(Of String)(0.7)
        r.Value1 = "Event1"
        r.Value2 = "Event2"

        Dim N1 As Int32 = 0
        For j As Int32 = 1 To N
            If r.NextValue() = "Event1" Then
                N1 += 1
            End If
        Next
        Console.WriteLine("p=" & (N1 / N).ToString())
        Console.WriteLine("q=" & (1.0 - (N1 / N)).ToString())
    End Sub
Private Sub TestDiscrete(ByVal N As Int32) Dim r As New Base.Mathematics.Probability.TDiscreteGenerator(Of String) r.Add(0.1, "Event1") r.Add(0.1, "Event2") r.Add(0.25, "Event3") r.Add(0.15, "Event4") r.Add(0.05, "Event5") r.AddLast("Event6") Dim freqData As New Dictionary(Of String, Int32) For j As Int32 = 1 To 6 freqData.Add("Event" & j.ToString(), 0) Next For j As Int32 = 1 To N freqData(r.NextValue()) += 1 Next For Each kv As KeyValuePair(Of String, Int32) In freqData Console.WriteLine(kv.Key & " p=" & (kv.Value / N).ToString()) Next End Sub Sub Main() Dim N As Int32 = 1000000 TestBernoulli(N) 'TestDiscrete(N) Console.ReadLine() End Sub End Module

 

   

Histogram of normally distributed random value:

 


Sample program that uses .NET random values to reorder sequence of integers.

 
Module Module1

    Public Class TMyComparer : Implements IComparer(Of KeyValuePair(Of Int32, Int32))

        Public Function Compare(ByVal x As System.Collections.Generic.KeyValuePair(Of Integer, Integer), _
            ByVal y As System.Collections.Generic.KeyValuePair(Of Integer, Integer)) As Integer _
            Implements System.Collections.Generic.IComparer(Of System.Collections.Generic.KeyValuePair(Of Integer, Integer)).Compare
            Return x.Value - y.Value
        End Function
    End Class


    Private Sub Mixer(ByVal sequence As List(Of Int32))
        Dim r As New Random((DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second) * 1000 + DateTime.Now.Millisecond)
        Dim mixsequence As New List(Of KeyValuePair(Of Int32, Int32))
        For Each N As Int32 In sequence
            mixsequence.Add(New KeyValuePair(Of Int32, Int32)(N, r.Next()))
        Next
        mixsequence.Sort(New TMyComparer)
        For j As Int32 = 0 To sequence.Count - 1
            sequence(j) = mixsequence(j).Key
        Next
    End Sub

    Sub Main()

        Dim sequence As New List(Of Int32)
        Dim N As Int32 = 10
        While N > 0
            sequence.Add(sequence.Count + 1)
            N -= 1
        End While

        Mixer(sequence)

        For Each j As Int32 In sequence
            Console.WriteLine(j)
        Next
        Console.ReadLine()
    End Sub

End Module
 

Plane clusters created using two normal random number generators (two generators - one for X, one for Y):

 


And sample code to generate 2D clusters:

 
Public Sub GenerateDataSet()

        Data = New List(Of TVector)

        ' 1 set
        Dim g1x As New Base.Mathematics.Probability.TNormalGenerator(1, 2)
        Dim g1y As New Base.Mathematics.Probability.TNormalGenerator(1, 0.4)
        Dim N As Int32 = 1070
        While N > 0
            Data.Add(New TVector(g1x.NextValue(), g1y.NextValue()))
            N -= 1
        End While

        Dim arr As New List(Of Base.Common.Pair(Of Double, Double))
        For Each v As TVector In Data
            arr.Add(New Base.Common.Pair(Of Double, Double)(v.X, v.Y))
        Next
        Dim Mxy As Base.Common.Pair(Of Double, Double) = Base.Mathematics.Probability.Functions.MeanValue(arr)
        Debug.Print("Mx1=" & Mxy.First.ToString() & vbCrLf)
        Debug.Print("My1=" & Mxy.Second.ToString() & vbCrLf)
        Dim sxy As Base.Common.Pair(Of Double, Double) = Base.Mathematics.Probability.Functions.StdDev(arr, Mxy)
        Debug.Print("sx1=" & sxy.First.ToString() & vbCrLf)
        Debug.Print("sy1=" & sxy.Second.ToString() & vbCrLf)


        N = 2000
        Dim g2x As New Base.Mathematics.Probability.TNormalGenerator(5, 1)
        Dim g2y As New Base.Mathematics.Probability.TNormalGenerator(5, 2)
        While N > 0
            Data.Add(New TVector(g2x.NextValue(), g2y.NextValue()))
            N -= 1
        End While
    End Sub