Excel的随机数生成器根本不随机?

时间:2017-04-29 02:29:18

标签: vba testing statistics

我正在试图测试Excel的随机生成器是否随机,这就是我使用Wald's test的原因

通过这样做,我得到了p值为0,因此我不接受零假设,即样本不是随机的。

我的问题是:

1)我在编码或解释时犯了一些错误吗?

代码:

    'By Julio Jesús Luna moreno
'jlqmoreno@gmail.com
Option Base 1
Sub WALDTEST()
 Dim x, r(), i, n, mu, sigma, plus(), minus(), k, h, f, j, phi, rplus, rminus, rachas() As Variant
  Dim w As Double, flag As Boolean
  n = 1000: k = 0: h = 0: flag = False: rplus = 0: rminus = 0: j = 0: phi = 0
  Set f = Application.WorksheetFunction
  ReDim r(n)
  For i = 1 To n
  Randomize
   x = Rnd()
    r(i) = IIf(x >= 0.5, 1, 0)
     Debug.Print r(i)
 Next i
  k = r(1)
   h = 2
 Do While h <= n
  Do Until flag = True Or h > n
    If r(h) = k Then
      phi = phi + 1
     flag = False
      h = h + 1
      Else
       flag = True
       k = r(h)
       h = h + 1
    End If
   Loop
    If phi >= 1 Then
     j = j + 1
      ReDim Preserve rachas(j)
       rachas(j) = r(h - 2)
        Debug.Print rachas(j)
     End If
     flag = False
    phi = 0
 Loop
rplus = j - f.Sum(rachas)
rminus = j - rplus
  mu = ((2 * rplus * rminus) / j) + 1
  sigma = (mu - 1) * (mu - 2) / (j + 1)
   w = f.Norm_S_Dist((j- mu)/Sqr(sigma), False)
    Debug.Print w
End Sub

提前致谢!

4 个答案:

答案 0 :(得分:1)

  • Excel的随机数生成器不是随机的 - 它只是伪随机数。 (与大多数计算机生成的随机数一样。)

  • 虽然随机数通常在大多数情况下都是随机数,但如果您编写一些代码,您可以看到数字的非随机性如下:

    Sub RndTest()
        Dim r(0 To 9999, 0 To 9999) As Long
        Dim i As Long
        Dim x As Long
        Dim y As Long
        For i = 1 To 100000000
            x = Int(Rnd() * 10000)
            y = Int(Rnd() * 10000)
            r(x, y) = r(x, y) + 1
        Next
        Cells(1, 1).Resize(10000, 10000).Value = r
        Columns("A:NTP").AutoFit
    End Sub
    

    运行该代码时需要有点耐心,因为它会生成100,000,000对随机数并将它们分配给100,000,000个单元。但最终产生的模式非常好看。

    很多问题是由于VBA中产生的随机数只是一个Single精度数,只包含大约6位有效数字。

答案 1 :(得分:1)

您需要对代码进行更正。您对mu的计算不正确。

mu = ((2 * f.Count(plus) + f.Count(minus)) / n) + 1

应该是

mu = ((2 * f.Count(plus) * f.Count(minus)) / n) + 1
                         ^
                         Note the change here

我认为你的randomize命令也应该在for / next循环之外和之前发生。我不确定这会对结果产生什么影响。

答案 2 :(得分:1)

这是如何实施的。

1-我们计算运行次数;每次观察到拾取值的翻转时递增

2-我们检查对应于独立假设(零假设)的正态分布的结果运行次数。确切地说,我们希望获得的运行次数的概率是&#34;那么远#34;从平均水平。注意:它是双尾测试

3-要计算此概率,您应使用累积正态分布,即将参数cumulative设置为true

Sub WaldWolfowitz()
  Randomize
  Dim nRuns As Long ' counts the number of runs
  Dim x As Long ' a randomly picked value, 0 or 1
  Dim lastX As Long: lastX = -1 ' memorizes the last picked value to count runs
  Dim N(0 To 1) As Long ' array holds the number of picks of 0 and 1

  Dim i As Long, r As Double
  For i = 1 To 1000000
    r = Rnd
    x = IIf(r < 0.5, 0, 1)
    N(x) = N(x) + 1
    If x <> lastX Then nRuns = nRuns + 1
    lastX = x
  Next

  ' Distribution of the number of runs in the case
  ' the picks are independent (the null hypothesis)
  Dim mu As Double, variance As Double, sigma As Double, p As Double, z As Double
  mu = 1 + ((2 * N(0)) / (N(0) + N(1))) * N(1) ' rewrote it this way to avoid overflow
  variance = (mu - 1) * (mu - 2) / (N(0) + N(1) - 1)
  sigma = Sqr(variance)

  ' The p-test. We calculate the likelihood that the resulted number of runs
  ' be "that far" from mu. Notice in the calculation below:
  ' - We compute the absolute value of diff because it's a "two-tailed test"
  ' - We calculate the "tail" area under the normal curve from that point
  '   and we multiply it by two
  ' - The parameter "Cumlative:=True" for Norm_S_Dist to calculate the area under the normal curve
  z = Abs(nRuns - mu) / sigma
  p = 2 * (1 - WorksheetFunction.Norm_S_Dist(z, True))

  Debug.Print mu, sigma, nRuns, z, p
End Sub

P.S。我会留给你运行测试并解释它们。我自己的测试并没有拒绝零独立假设,尽管我对它们并不满意......

答案 3 :(得分:0)

您也可以通过掷硬币进行测试。

  

Actual experiments have shown that the coin flip is fair up to two decimal places and some studies have shown that it could be slightly biased

以下是测试此功能的快速代码(不是最好的,但按预期工作):

Sub flip()
Dim coin, i, j, zero, one, totalzero, totalone As Long
Dim averagezero, averageone As Double
For i = 1 To 100
    For j = 1 To 100
        coin = WorksheetFunction.RandBetween(0, 1)
        If Value = 0 Then
            zero = zero + 1
        ElseIf Value = 1 Then
            one = one + 1
        End If
    Next j
    totalzero = totalzero + zero
    totalone = totalone + one
    zero = 0
    one = 0
Next i
averagezero = totalzero / 100
averageone = totalone / 100
Debug.Print "Average Zero Count: " & averagezero
Debug.Print "Average One Count: " & averageone
End Sub