尝试使用Excel VBA模拟彩票抽奖时的问题(抽奖时不回抽号码)

时间:2019-02-27 17:57:48

标签: excel vba random

我试图使用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

1 个答案:

答案 0 :(得分:1)

VBA中的随机数(以及几乎每种语言中的随机数)实际上并不是随机的。他们是伪随机的。软件不能凭空获取随机数,它们必须来自某种算法。该算法需要一些输入值。随机数生成器的输入值称为种子。随机数算法将始终为给定的输入种子生成相同的“随机”值。

如果您查看documentation for Randomize,则如果不提供种子值,它将从系统时间获取它。由于循环发生得非常快,因此某些迭代的系统时间最终是相同的,并且Randomize设置的种子与上次循环完全相同。

因此,您只想在程序开始时调用一次Randomize函数,而不是在每个循环中调用一次。这样可以确保您的总体结果是随机的(如果未调用Randomize函数,则Rnd将从Rnd返回的值用作其输入种子。)