【问题标题】:Funny Behavior of Excel VBA Random number routineExcel VBA随机数例程的有趣行为
【发布时间】:2012-06-19 21:13:46
【问题描述】:

我正在尝试通过以下算法通过 vba 生成一堆随机排列:

Function RandNumber(Bottom As Integer, Top As Integer, _
                Amount As Integer) As Integer()

Dim iArr As Variant
Dim i As Integer
Dim r As Integer
Dim temp As Integer
Dim bridge() As Integer

'Application.Volatile

ReDim iArr(Bottom To Top)

For i = Bottom To Top
    iArr(i) = i
Next i
Randomize
For i = Top To Bottom + 1 Step -1
    r = Int(Rnd() * (i - Bottom + 1)) + Bottom
    temp = iArr(r)
    iArr(r) = iArr(i)
    iArr(i) = temp
Next i

ReDim Preserve bridge(1 To Amount)
For i = Bottom To Bottom + Amount - 1
    bridge(i - Bottom + 1) = iArr(i)
Next i

RandNumber = bridge

End Function

RandNumber 本质上所做的是它根据用户提供的底部和顶部值随机给出一个排列。示例 RandNumber(1,2,1) 将是 12 随机。

现在我通过下面的例程来测试这个功能

Sub RandNos()
Dim z() As Variant
ReDim Preserve z(1 To 2560)
For i = 1 To 2560
z(i) = RandNumber(1, 2, 1)
Next i
For i = 1 To 2560
ThisWorkbook.Sheets("Sheet2").Range("A1").Offset(i - 1, 0) = z(i)
Next i
End Sub

令我大吃一惊的是,“随机”数字在 256 次运行后正好重复! (好吧,上面i 的值 2560 并不完全是巧合。我怀疑在 256 行左右存在某种模式,因此将 i 视为 2560)

此外,当我用稍微修改的子程序测试上述功能时:

Sub RandNos2()
For i = 1 To 2560
ActiveSheet.Range("A1").Offset((i - 1) Mod 256 + 1, Int((i - 1) / 256)) = RandNumber(1, 2, 1)
Next i
End Sub

模式消失了。 (至少在 256 个值之后重复的模式消失了。不确定是否会出现另一种模式)。

现在根据我的知识,Randomize 应该通过生成适当的种子来控制随机性,而 vba 中的Randomizetimer 中获取种子。所以我的假设是,在第一个RandNos() 子例程中,更新发生得如此之快,以至于种子的更新速度不够快。当我使用第二个子例程进行测试时,模式消失的事实,因为在第二个例程中,excel 在工作表中编写代码需要更长的时间,因此给了代码一些机会来更新 timer 和它的种子随机数 - 支持我的假设。

所以我的问题是

  1. 我的假设是否正确。
  2. 我是否仍希望在 Excel VBA 中生成“随机”模式。
  3. 我在这里用错了Randomize

提前感谢您对此问题的建议。

编辑:评论中的建议之一是我们应该只调用一次Randomize。我试过这样做,它似乎工作。但是,我仍然想知道使用上面的Randomize 出了什么问题。

【问题讨论】:

  • 我很确定你应该只调用一次 Randomize。
  • 我正在运行一个重复调用这个函数的子程序,那么Randomize应该在sub而不是function
  • 是的。或者如果 RandNos() 被多次调用,则在调用 RandNos() 的方法中使用 Randomize。
  • @AndrewMorton,我鼓励你发布这个作为答案。
  • @DougGlancy 鼓励屈服于。

标签: vba random excel


【解决方案1】:

您应该只拨打一次Randomize。如果RandNos()被多次调用,则在调用RandNos()的方法中使用Randomize

【讨论】:

  • 感谢您的回答.. 但是为什么在上面的例程RandNos2 中看不到该模式,为什么在 256 之后重复?
【解决方案2】:

在回答您的编辑时,您的假设是正确的。

Randomize 花费时间并生成种子。种子只是 Rnd 的起点。

但是,请注意 Randomize 不是一对一的,这意味着对于 Randomize 的任何给定输入(即时间),它不会每次都生成相同的种子。您似乎已经发现 Randomize 对于每个给定的输入都有一个由 256 个种子组成的序列。因此,您会得到一个包含 256 个数字的重复序列,这些数字本来应该是随机的,但显然不是。

参考:Randomize 和 CS 类的 VBA 帮助页面

【讨论】:

    【解决方案3】:

    为了以后遇到此问题的其他人参考,我决定尝试“减慢”子例程,使其足以让系统计时器重置。 Application.wait 效果不佳,但我发现通过在 randomize 调用上方包含一个简单的 debug.print 行,它会减慢执行速度,使其不会每 256 次重复一次。这不会显着增加整体运行子程序的时间。对于那些不介意牺牲一点优化以非常简单地修复伪随机性的人来说,这只是一个想法。

    【讨论】:

    • 仅供参考,我不知道您使用了什么时间,但是,Application.Wait(Now + TimeValue("00:00:01")) 效果不佳,因为它实际上从(非常接近)零到 1 秒等待。见stackoverflow.com/questions/1544526/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多