尝试使用Excel VBA模拟抽奖时绘制的问题(不放回数字)

问题描述 投票:0回答:1

我试图使用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
excel vba random
1个回答
1
投票

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

如果你查看documentation for Randomize,如果你没有提供种子值,它会从系统时间得到它。由于循环非常快速地发生,因此对于某些迭代,系统时间最终是相同的,而Randomize只是将种子设置为与上一循环中完全相同。

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

© www.soinside.com 2019 - 2024. All rights reserved.