Link to home
Start Free TrialLog in
Avatar of deming
deming

asked on

Random Number Generator for PHP and VB6 (PRNG)

I need to be able to provide a seed to a pseudo random number generator (PRNG) in both VB6 and PHP (Linux) and have the results be identical. Thus I cannot use the built in VB6 random function because PHP does not use the same PRNG algorithm. Thus the same seed in VB6 does not generate the same sequence as in PHP.

Previously EE "htamas" suggested the below, however, it is not working when I do the following:

Bnd(-67)
Bandomize(67)
a=bnd()

rnd(-67)
Randomize(67)
b=rnd()

If a<>b then debug.print "ERROR: Sequence is not the same"

*** QUESTION:  ***
Can anyone see what is wrong in htamas code below?



EE "htamas" solution which is close but not working:
--------------------------------------------------------------
Here is the 'source code' for the VB6 built-in Random and Randomize functions. Not the actual source, but one that's identical to it:

Dim Lastband As Long

Sub Initbandom() 'This is run on startup
Lastband = &H50000
End Sub

Sub Bandomize(Optional Number)
Dim seed As Long
Dim cnum As Single
Dim bits As Long
Dim mask As Long
Dim i As Integer
If IIf(IsError(Number), 1, Number) = 0 Then
  seed = 0
Else
  cnum = IIf(IsError(Number), Timer, Number)
  seed = &H4130
  Do While cnum >= 2097152
    cnum = cnum / 2
    seed = seed + &H10
  Loop
  Do While cnum < 1048576
    cnum = cnum * 2
    seed = seed - &H10
  Loop
  bits = Int(cnum)
  mask = 1
  For i = 1 To 20
    If bits And 1 Then
      seed = seed Xor mask
    End If
    bits = bits \ 2
    mask = mask * 2
    If i = 16 Then mask = 1
  Next i
End If
Lastband = seed * &H100 + Lastband Mod &H100
End Sub

Function Bnd(Optional Number) As Single
Dim p As Long, q As Long, j As Long
If IIf(IsError(Number), 1, Number) > 0 Then
  p = Lastband
  q = &HFD43FD
  d = &HC39EC3
  For j = 1 To 6
    d = (d + (p Mod &H10) * q) Mod &H1000000
    p = p \ &H10
    q = (q * &H10) Mod &H1000000
  Next j
  Lastband = d
ElseIf Number < 0 Then
  Lastband = &H395886
End If
Bnd = CSng(Lastband) / &H1000000
End Function
 
Avatar of Ark
Ark
Flag of Russian Federation image

Hi
Her is a brief explanation how Rnd and Randomize works:

Rnd algorithm is well-known and published by MS at http://support.microsoft.com/kb/q231847/
Though MS claimed, that
>>Note that the above algorithm cannot be implemented in Visual Basic code in such a way that the random number sequence generated by the RND function can be reproduced.<<
in this article, here is VB implementation of this:

'=======bas module====
Private Const rnd_initial = 327680    '&H50000

Private Const two12 = &H1000&         '2^12
Private Const two12minus1 = two12 - 1 '2^12-1
Private Const two24 = two12 * two12   '2^24
'MS values for a and c coeff and their lo/high words to avoid overflow error when rnd calculated
Private Const a = 1140671485, a0 = a Mod two12, a1 = a \ two12
Private Const c = 12820163, c0 = c Mod two12, c1 = c \ two12
   
Dim last_value As Double  'Unfortunately, VB doesn't allow init it here, like
                          'Dim last_value As Double = rnd_initial
Dim bRandomize As Boolean 'so we need this trick with boolean variable

Public Function My_Rnd(Optional ByVal number As Single = 1) As Single
   If Not bRandomize And (last_value = 0) Then 'Init last_value with default &H50000
      last_value = rnd_initial
      bRandomize = True
   End If
   If number = 0 Then 'Rnd(0) simply return last Rnd
      My_Rnd = last_value / two24
      Exit Function
   End If
   If number < 0 Then 'TODO: Implement MS Xored algorithm
      MsgBox "Not implemented yet"
   End If
   Dim s0 As Long, s1 As Long
   s0 = last_value And two12minus1
   s1 = last_value \ two12
   last_value = (s0 * a0 + c0) + ((s0 * a1 + s1 * a0 + c1) And two12minus1) * two12
   If last_value > two24 Then last_value = last_value - two24
   My_Rnd = last_value / two24
End Function

'========Form code======
Private Sub Command1_Click()
   For i = 1 To 100
      a = My_Rnd
      b = Rnd
      Debug.Print a, b, IIf(a = b, "Same", "Not same")
   Next i
End Sub

'=============================================================
So you can see that both code produce same sequence.
But MS add additional futures to its PRNG:

1. Call Rnd with negative number return some value, using MS crypto algorithm (not published). IMHO this is some simple Xored algorithm, I'll try to disassembly VBA6.dll and/or msvbvm and find this algorithm. In this case last_value doesn't change.
2. Randomize also use same (or near same) algorithm to set initial last_value for futher sequence.

So, if you need just normal distribution for some math investigations - use above code. If you try to use Rnd for some crypto algorithm - I suggest you choose another algorithm (BTW, Mersenne Twister also isn't the best (though better then VB Rnd), since it's well-known)
ASKER CERTIFIED SOLUTION
Avatar of Ark
Ark
Flag of Russian Federation image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hehe, some words about "RANDOM" M$ algorithm :)

Private Sub Command1_Click()
   Randomize Timer
   Debug.Print "=================="
   Debug.Print "And now, how [random] it is:"
   Debug.Print "Using a=2^24*Rnd And 3"
   Debug.Print "=================="
   For i = 1 To 100
      a = 2 ^ 24 * Rnd And 3
      Debug.Print a & ", ";
   Next i
End Sub

'Output:
3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0

Seems VERY (!) {random} :)))))
BTW, just courious, why do you need PHP script code if you already have VB one? Just create ActiveX dll, smth like

Public Sub DoRandomize(Optional val)
   Randomize val
End Sub

Public Function DoRnd(optional val)
   DoRnd = Rnd(val)
End Function

And then, in PHP

$o = New COM("YourRndClassName");
$o->DoRandomize();
echo $o->DoRnd();

PS
To much more secure random sequense, you can use

Private Declare Function CryptGenRandom Lib "advapi32.dll" _
(ByVal hProv As Long, ByVal dwLen As Long, ByVal pbBuffer As String) As Long