Xtreme Visual Basic Talk

Xtreme Visual Basic Talk (http://www.xtremevbtalk.com/)
-   Code Library (http://www.xtremevbtalk.com/code-library/)
-   -   Random Number - Normal Distribution revisited (http://www.xtremevbtalk.com/code-library/326948-random-normal-distribution-revisited.html)

loquin 03-27-2014 03:23 PM

Random Number - Normal Distribution revisited
 
I recently moved the Box-Muller (Polar) algorithm for transforming MS's standard random number to a random number with a normal distribution to dotNet. (Did not make a class of it; just migrated the code module)

Original, VB6 thread from the Code Library.


A discussion of the algorithm is available here

Code:

Imports System.Math

Module GAUSSE

    Public Sub Init()
        Static Initialized As Boolean = False

        If Not Initialized Then
            Randomize()
            Initialized = True
        End If

    End Sub

    Public Function Normal(Optional ByVal Sigma As Double = 1.0#, Optional ByVal Mean As Double = 0.0#) As Double
        Dim Norm As Double = 0.0#

        Norm = (GetGausse() * Sigma) + Mean

        Return Norm

    End Function

Private Function GetGausse() As Double
        ' This Function returns a standard Gaussian random number, based upon the polar form of the Box-Muller transform.

        ' since this calc is capable of returning two calculations per call, it's been written to save the second calc for the next
        ' pass through the function, thus saving some time on alternate calls.

        ' Only call the Init sub once in the life of the app, but it's only needed if you haven't seeded the random number generator already.
        ' NEVER call INIT or randomize within a loop, or as a function of time - this can seriously degrade the 'randomness' of the data.
        ' (The reason for this is that the randomize function reseeds the random number generator with a seed derived from the system clock. 
        '  If you call randomize within a loop, especially a tight loop, the odds are good that you will repeatedly reseed the random number
        '  generator with the SAME seed as the prior call - which results in the same 'random' number being generated...)  If randomize is called
        '  once from an event that depends upon operator input, such as a form load event, you would be OK, however.
        '
        Static blReturn2 As Boolean = True  ' Flag to calc new value pair, or return previously calculated second normal rnd. 
        Static dblReturn2 As Double = 0.0#  ' Alternate return value

        Dim Work1 As Double = 0.0#, Work2 As Double = 0.0#, Work3 As Double = 0.0#

        Const Two As Double = 2.0#, One As Double = 1.0#

        blReturn2 = Not blReturn2 '  toggle the return value source flag

        If blReturn2 Then  ' On odd numbered calls
            Return dblReturn2
        Else
            Work3 = Two
            Do Until Work3 < One
                Work1 = Two * Rnd() - One
                Work2 = Two * Rnd() - One
                Work3 = Work1 * Work1 + Work2 * Work2
            Loop
            Work3 = Sqrt((-(Two) * Log(Work3)) / Work3)

            ' a second valid value will be returned with Work2 * Work3. Calculate & save in static variable for the next pass, for efficiency.
            dblReturn2 = Work2 * Work3

            Return Work1 * Work3

        End If

    End Function
End Module

Note: I did add a subroutine version of both the GetGausse and Normal functions. This allows you to return both available results in one call, and you don't need the static variables in the subs. These two changes resulted in a 40% performance improvement. (0.15 seconds down to .09 seconds per million numbers retrieved.)

loquin 06-19-2014 01:07 PM

And, here is the code for the SUB version. Declare two double variables in your calling code, then use them as parameters to Normal2, as per below. As always, never call randomize more than once in the life of your app.

Code:
Dim Return1, Return2 as Double, Mean as Double = 100.0, Sig as Double = 2.5 Randomize Normal2(Return1, Return2, Sig, Mean) ' Return1 and Return2 now contain a pair of normally distributed random numbers, ' weighted to have a mean of 100 with a sigma of 2.5
Code:
' ******************************************************************************************************************* ' *** GetGausse2 written as a sub, to return 2 values per call, instead of declaring static variables and *** ' *** storing second value for the alternate calls as in GetGausse. This results in more than 40% improvement *** ' *** in performance (Normal: 0.160 seconds per million calls, Normal2: 0.094 seconds per million calls on my *** ' *** current PC, VB.Net v12) - loquin *** ' ******************************************************************************************************************* Public Sub Normal2(ByRef Norm1 As Double, ByRef Norm2 As Double, Optional ByRef Sigma As Double = 1.0#, Optional ByRef Mean As Double = 0.0#) Dim t1, t2 As Double Call GAUSSE.GetGausse2(t1, t2) Norm1 = t1 * Sigma + Mean Norm2 = t2 * Sigma + Mean End Sub Private Sub GetGausse2(ByRef Value1 As Double, ByRef Value2 As Double) Dim Work1 As Double = 0.0#, Work2 As Double = 0.0#, Work3 As Double = 0.0# Const Two As Double = 2.0#, One As Double = 1.0# Work3 = Two Do Until Work3 < One Work1 = Two * Rnd() - One Work2 = Two * Rnd() - One Work3 = Work1 * Work1 + Work2 * Work2 Loop Work3 = Sqrt((-(Two) * Log(Work3)) / Work3) Value1 = Work1 * Work3 Value2 = Work2 * Work3 End Sub


All times are GMT -6. The time now is 08:52 AM.

Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Search Engine Optimisation provided by DragonByte SEO v2.0.15 (Lite) - vBulletin Mods & Addons Copyright © 2017 DragonByte Technologies Ltd.
All site content is protected by the Digital Millenium Act of 1998. Copyright©2001-2011 MAS Media Inc. and Extreme Visual Basic Forum. All rights reserved.
You may not copy or reproduce any portion of this site without written consent.