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 |
![]() |
![]() |
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
|