简体   繁体   English

Excel VBA的滑稽行为随机数例程

[英]Funny Behavior of Excel VBA Random number routine

I am trying to generate a bunch of random permutations via the the following algorithm through vba: 我正在尝试通过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

What RandNumber essentially does is that it randomly gives a permutation based on the bottom and top values provided by the user. RandNumber本质上所做的就是根据用户提供的最低和最高值随机给出一个排列。 Example RandNumber(1,2,1) will either be 1 or 2 randomly. 示例RandNumber(1,2,1)将随机为12

Now I am testing this function through the following routine 现在,我通过以下例程测试此功能

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

To my utter amazement the 'random' numbers are repeating exactly after 256 runs! 令我惊讶的是,“随机”数在256次运行后重复出现! (Ok, the value 2560 for i above is not exactly a co-incidence. I was suspecting that there is some pattern going on around 256 rows and thus took i as 2560) (好吧,上面的i的值2560并不完全是巧合。我怀疑256行附近存在某种模式,因此将i设为2560)

Moreover, when I test the above function with slightly modified subroutine: 而且,当我用稍微修改的子例程测试以上功能时:

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

the pattern is gone. 模式消失了。 (At least the pattern of repeating after 256 values is gone. Not sure if another pattern emerges). (至少在256个值之后重复的模式消失了。不确定是否出现另一个模式)。

Now based on my knowledge the Randomize is supposed to control the randomness by generating appropriate seeds and Randomize in vba takes the seed from the timer . 现在根据我的知识, Randomize应该通过生成适当的种子来控制随机性,而vba中的Randomizetimer获取种子。 So my hypothesis is that in the 1st RandNos() subroutine the updates happen so quickly that the seeds don't get updated fast enough. 因此,我的假设是,在第一个RandNos()子例程中,更新是如此之快,以至于种子无法足够快地被更新。 The fact that pattern is gone when I test with the second subroutine, since in the second routine excel takes longer to write the code in the worksheet and hence the gives the code some chance to update the timer and with it the seed of the Random numbers - supports my hypothesis. 当我使用第二个子例程进行测试时,模式消失了,因为在第二个例程中excel需要更长的时间才能在工作表中编写代码,因此使代码有机会更新timer以及随机数的种子-支持我的假设。

So my question here is 所以我的问题是

  1. Is my hypothesis correct. 我的假设正确吗?
  2. Should I still hope to generate a 'random' pattern in Excel VBA. 我是否仍希望在Excel VBA中生成“随机”模式。
  3. Am I making a wrong use of Randomize here 我在这里错误地使用了Randomize

Thanks in advance for your suggestions on the issue. 预先感谢您对这个问题的建议。

EDIT: One of the suggestions in the comment was that we should call Randomize only once. 编辑:评论中的建议之一是,我们应该只调用一次Randomize I tried doing that and it seems to work. 我尝试这样做,它似乎起作用。 However, I would still like to know what goes wrong using Randomize as above. 但是,我仍然想知道使用如上所述的Randomize出了什么问题。

You should call Randomize once only. 您应该只调用一次Randomize If RandNos() is called more than once, use Randomize in the method that calls RandNos() . 如果RandNos()调用RandNos() ,请在调用RandNos()的方法中使用Randomize

in answer to your edit, your hypothesis is correct. 回答您的编辑,您的假设是正确的。

Randomize takes the time and generates a seed. 随机化会花费时间并生成种子。 A seed is just a starting point for Rnd. 种子只是Rnd的起点。

However, note that Randomize is not one-to-one which means that for any given input into Randomize (ie the time) it doesn't generate the same seed every time. 但是,请注意,Randomize不是一对一的,这意味着对于任何给定的Randomize输入(即时间),它不会每次都生成相同的种子。 You appear to have discovered that Randomize has a sequence of 256 seeds for every given input. 您似乎已经发现,对于每个给定的输入,“随机化”都有256个种子的序列。 Therefore, you get a repeating sequence of 256 numbers which were supposed to be random but which clearly are not. 因此,您得到一个256个数字的重复序列,这些序列本来是随机的,但显然不是。

Reference: The VBA help page for Randomize and CS classes 参考:有关Randomize和CS类的VBA帮助页面

just for future reference to anyone else who encounters this problem, I decided to just try "slowing down" the subroutine enough to allow the system timer to reset. 为了将来对遇到此问题的任何其他人提供参考,我决定尝试尝试“减慢”该子例程,使其足以重置系统计时器。 Application.wait did not work well, but I found that by including a simple debug.print line above the randomize call, it slowed the execution down just enough to get it to not repeat every 256. This not dramatically increase the overall run time of the subroutine. Application.wait不能很好地工作,但是我发现通过在randomize调用上方包含一条简单的debug.print行,它减慢了执行速度,足以使其不再每256次重复。这不会显着增加的整体运行时间。子例程。 Just a thought for folks who would not mind sacrificing a little bit of optimization for a very simple fix on the pseudo-randomness. 对于那些不愿意为了优化伪随机性而牺牲一点优化的人来说,这只是一个想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM