我试图使用VBA子程序模拟649抽奖。对于彩票抽奖,将通过球机选择六个球,开始时有49个球,每个球的选择概率为1/49,并且在选择第一个球之后,其余48个球将各自具有被选中的概率为1/48,依此类推。
没有直接的VBA函数来生成随机数,使得区间不连续;例如,选择的第一个数字是3,而对于第二个数字选择,3将不可用!所以计算机必须选择1,2,4,...,49。
下面是我写的子程序,基本上我使用Int((UBound(数组) - 1 + 1)* Rnd + 1)首先在整数间隔之间生成随机数,但我只将随机数视为索引;例如,对于第二个数字选择我剩下上面48个数字:1,2,4,...,49,现在如果随机数是3(从1到48之间选择),我实际得到4第二个数字选择,因为它是列表中的第3个。并且Rnd()提供均匀分布的抽取,因此每个数字同样可能。这是我用来绕过的方法。
然后我将所有先前选择的数字记录到s1到s6中,然后在随后的数字选择中使它们不重复。
最后,我使用在VBA array sort function?上找到的快速排序算法进行排序,稍微修改输入数组。并在空工作表上输出结果。
我还使用Randomize来增加随机性。所以一切看起来都不错,我正好模仿球机:选择第一个数字,然后是第二个......最后是第六个,没有放回(非重复),唯一的区别我认为是球机是真随机数,而VBA是伪随机数。
令我惊讶的是,对于100,000次模拟,我使用了Remove Duplicates,然后找到并删除了79994个重复值; 20006独特价值仍然存在。现在我觉得它不可靠。大多数抽奖怎么可能有重复?试过很多次但是同样的事情,很多重复。我不知道哪里出了问题,如果这个设计和逻辑有问题,或者只是因为伪随机数?谢谢你们!
这是我的代码:
Public k As Long
Sub RNG()
Dim NUMBER(), SELECTION(1 To 100000, 1 To 6)
Dim i As Integer, j As Integer, n As Integer
Dim s1 As Integer, s2 As Integer, s3 As Integer, s4 As Integer, s5 As Integer, s6 As Integer
For k = 1 To 100000
Erase NUMBER
ReDim NUMBER(1 To 49)
For i = 1 To 49
NUMBER(i) = i
Next i
For j = 1 To 6
'generate random number as index and select number based on index
Randomize
random_number = Int((UBound(NUMBER) - 1 + 1) * Rnd + 1)
SELECTION(k, j) = NUMBER(random_number)
'record each selection
Select Case j
Case Is = 1
s1 = SELECTION(k, j)
Case Is = 2
s2 = SELECTION(k, j)
Case Is = 3
s3 = SELECTION(k, j)
Case Is = 4
s4 = SELECTION(k, j)
Case Is = 5
s5 = SELECTION(k, j)
Case Is = 6
s6 = SELECTION(k, j)
End Select
'recreate number 1 to 49 by excluding already-selected numbers
Erase NUMBER
ReDim NUMBER(1 To 49 - j)
n = 0
For i = 1 To 49
Select Case j
Case Is = 1
If i <> s1 Then
n = n + 1
NUMBER(n) = i
End If
Case Is = 2
If i <> s1 And i <> s2 Then
n = n + 1
NUMBER(n) = i
End If
Case Is = 3
If i <> s1 And i <> s2 And i <> s3 Then
n = n + 1
NUMBER(n) = i
End If
Case Is = 4
If i <> s1 And i <> s2 And i <> s3 And i <> s4 Then
n = n + 1
NUMBER(n) = i
End If
Case Is = 5
If i <> s1 And i <> s2 And i <> s3 And i <> s4 And i <> s5 Then
n = n + 1
NUMBER(n) = i
End If
End Select
Next i
Next j
Call QuickSort(SELECTION, 1, 6)
Next k
Range("A1:F" & k - 1).Value = SELECTION
End Sub
Public Sub QuickSort(vArray As Variant, inLow As Long, inHi As Long)
'https://stackoverflow.com/questions/152319/vba-array-sort-function
Dim pivot As Variant
Dim tmpSwap As Variant
Dim tmpLow As Long
Dim tmpHi As Long
tmpLow = inLow
tmpHi = inHi
pivot = vArray(k, (inLow + inHi) \ 2)
While (tmpLow <= tmpHi)
While (vArray(k, tmpLow) < pivot And tmpLow < inHi)
tmpLow = tmpLow + 1
Wend
While (pivot < vArray(k, tmpHi) And tmpHi > inLow)
tmpHi = tmpHi - 1
Wend
If (tmpLow <= tmpHi) Then
tmpSwap = vArray(k, tmpLow)
vArray(k, tmpLow) = vArray(k, tmpHi)
vArray(k, tmpHi) = tmpSwap
tmpLow = tmpLow + 1
tmpHi = tmpHi - 1
End If
Wend
If (inLow < tmpHi) Then QuickSort vArray, inLow, tmpHi
If (tmpLow < inHi) Then QuickSort vArray, tmpLow, inHi
End Sub
VBA(以及几乎所有语言)中的随机数实际上并不是随机的;他们是伪随机的。软件无法从空中拉出随机数,它们必须来自某种算法;该算法需要一些输入值。随机数生成器的输入值称为种子。随机数算法将始终为给定的输入种子生成相同的“随机”值。
如果你查看documentation for Randomize,如果你没有提供种子值,它会从系统时间得到它。由于循环非常快速地发生,因此对于某些迭代,系统时间最终是相同的,而Randomize只是将种子设置为与上一循环中完全相同。
因此,您只想在程序开始时调用Randomize函数,而不是在每个循环期间调用。这可以确保您的总体结果是随机的(如果未调用Randomize函数,Rnd
使用从Rnd
返回的最后一个值作为其输入种子。)