我试图使用VBA子例程模拟649张彩票抽奖。 对于抽奖,将通过制球机选择6个球,开始时有49个球,每个被选择的概率为1/49,选择第一个球后,其余48个球将分别具有一个被选中的可能性为1/48,依此类推。
没有直接的VBA函数生成随机数,以使间隔不连续;例如,选择的第一个数字是3,而选择第二个数字,则3将不可用!因此计算机必须从1,2,4,...,49中进行选择。
下面是我编写的子例程,基本上我使用Int((UBound(array)-1 + 1)* Rnd + 1)首先生成整数间隔之间的随机数,但是我只将随机数视为索引;例如,对于第二个数字选择,我剩下上面的48个数字:1、2、4,...,49,现在如果随机数是3(从1到48之间选择),我实际上得到4第二个数字选择,因为它是列表中的第三个。而且Rnd()提供均匀分布的引数,因此每个数字的可能性均等。这是我用来解决问题的方法。
然后我将所有先前选择的数字记录到s1到s6中,然后在随后的数字选择中使它们不重复。
最后,我使用在VBA array sort function?处找到的快速排序算法对输入数组进行了少许修改。并在一个空的工作表上输出结果。
我还使用Randomize来增加随机性。所以一切似乎都很好,我在模仿球机的功能:选择第一个数字,然后选择第二个……最后是第六个,而不放回(非重复),我认为唯一的区别就是球机是True随机数,而VBA是伪随机数。
令我惊讶的是,对于100,000次模拟,我使用了删除重复项,然后找到并删除了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
答案 0 :(得分:1)
VBA中的随机数(以及几乎每种语言中的随机数)实际上并不是随机的。他们是伪随机的。软件不能凭空获取随机数,它们必须来自某种算法。该算法需要一些输入值。随机数生成器的输入值称为种子。随机数算法将始终为给定的输入种子生成相同的“随机”值。
如果您查看documentation for Randomize,则如果不提供种子值,它将从系统时间获取它。由于循环发生得非常快,因此某些迭代的系统时间最终是相同的,并且Randomize设置的种子与上次循环完全相同。
因此,您只想在程序开始时调用一次Randomize函数,而不是在每个循环中调用一次。这样可以确保您的总体结果是随机的(如果未调用Randomize函数,则Rnd
将从Rnd
返回的值用作其输入种子。)