【问题标题】:Why are my random numbers not changing accordingly in Excel?为什么我的随机数在 Excel 中没有相应变化?
【发布时间】:2021-12-10 15:34:38
【问题描述】:

下面有一些代码可以在 Excel 中生成一些随机数:

Sub Macro1()

Dim RA1 As Variant
ReDim RA1(1 To 5)

For i = 1 To 5

    Rnd (-1)
    Randomize i

    For j = 1 To 5
            
        RA1(j) = Rnd
                             
    Next j
    
    With Sheets("Sheet1")
    
        .Range(Cells(i, 1), Cells(i, 5)).Value = RA1
        
    End With
    
Next i

End Sub

此代码基本上生成 5 行,每行 5 个随机数,但它并没有完全按照应有的方式运行。当我在 iMac (2021) 上运行此代码时,每行中的随机数完全相同。但是,这段代码应该生成 5 行不同的随机数。

这就是事情变得更加奇怪的地方。当我在我的 Windows 笔记本电脑上运行此代码时,输​​出是所希望的 - 也就是说,我确实得到了 5 行不同的随机数。我已经和我的教授谈过这个问题,他也在他的 Windows 电脑上进行了尝试,得到了 5 行不同的随机数。

总而言之,我们基本上不知道为什么我的 iMac 无法运行这段代码。有没有人猜到这里为什么会出现这种差异?例如,我的 iMac 的 Excel 中是否有任何设置阻止我的代码正常运行?

任何解释和解决方案将不胜感激! :)

【问题讨论】:

  • 为什么不直接在每个单元格中使用随机函数或随机函数之间的函数?当工作表被告知重新计算时,它们会发生变化,因为它们是易变的。
  • @SolarMike 原因是因为这小部分代码是我需要运行的较大宏的一部分,所以无论如何我都必须使用 VBA 来执行此操作。另一件事是我需要播种我的随机数,这样对于每个 i,我都有不同的随机数,但是任何人运行我的宏时,他们每次都应该得到相同的随机数集。现在的问题是,当我在 iMac 上运行它时,对于每个 i,我都有相同的随机数集,这很奇怪。我只是想知道是否有人知道为什么它在我的 iMac 上运行时没有在 Windows 上正常运行。
  • 您是否关闭了其他宏之一中的计算?
  • @SolarMike 是的。因为这个宏运行 1000 个 i 值,所以我在实际的完整宏中将计算设置为手动,但是我在每个相关点都使用 .Calculate 吗?另外,如果我可以补充的话,没有“其他宏之一”。实际上只有一个。如果手动计算是个问题,那仍然不能解释为什么确切的代码在 Windows 上运行得很好?
  • 好吧,您显示的代码中没有 .Calculate 。而且您可能想在这里为“选项显式”做一些研究......

标签: excel vba macos random random-seed


【解决方案1】:

您描述的行为是一个错误。

首先,如果我们替换行:

Randomize i

与:

Randomize 0

我们可以看到,在 Windows 上,我们得到与在 Mac 上完全相同的重复值:

这立即向我暗示只能有两种可能的解释:

  1. 可能算法不同
  2. 存在问题,i 的值未正确传递/读取。

为了找到一个规则,我使用了一种单独的方法(蛮力 i/x)并找到了以下幻数。同样,如果我们替换行:

Randomize i

与:

#If Mac Then
    Dim arr() As Variant: arr = Array(26489, 63707, 185603, 15365, 92513)
    Randomize i / arr(i - 1)
#Else
    Randomize i
#End If

我们在 Windows 和 Mac 上得到相同的结果。

我在这些幻数中找不到清晰的模式,所以我放弃了算法不同的说法。这让我找到了问题/错误。

经过反复试验,我发现如果我们将 Double 数据类型传递给 Randomize 方法,它不会读取完整的 8 个字节,而只会读取前 4 个字节。这就是除以这些幻数起作用的原因,因为(这些除法的)结果数字使用前 4 个字节(包括指数位)而不是 8 个完整字节。

修复方法是将双精度值(向左)偏移 4 个字节。这是适用于 Windows 和 Mac 的最终代码:

Option Explicit

#If Mac Then
    #If VBA7 Then
        Public Declare PtrSafe Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As LongPtr) As LongPtr
    #Else
        Public Declare Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As Long) As Long
    #End If
#End If

Sub Macro1()
    Dim i As Long
    Dim j As Long
    Dim RA1 As Variant
    ReDim RA1(1 To 5)
    
    For i = 1 To 5
        Rnd (-1)
        
        #If Mac Then
            Dim d As Double
            
            d = CDbl(i)
            CopyMemory d, ByVal VarPtr(d) + 4, 4 'Read the last 4 double bytes into the first 4
            Randomize d
        #Else
            Randomize i
        #End If

        For j = 1 To 5
            RA1(j) = Rnd
        Next j
        
        With Sheets("Sheet1")
            .Range(Cells(i, 1), Cells(i, 5)).Value = RA1
        End With
    Next i
End Sub

您会注意到我还添加了Option Explicit,声明了所有变量并缩进了代码。

【讨论】:

  • 酷!非常感谢你做的这些。以我的 VBA 知识水平,我永远无法解决这种深度的问题。您是否碰巧知道这是否是 Apple 上的 VBA/Excel 的一个已知错误,因为 Apple 上的 VBA/Excel 无法读取 8 个字节?这个问题是否会出现在其他代码(与Randomize 无关)上,这些代码可能在 Windows 上运行得很好,但在 Apple 上却不行?
  • @EthanMark 我以前不知道这个问题,但它似乎与Randomize 方法和Mac 无关。我的猜测是,编写该方法的开发人员根本没有考虑到 Double 存储在 8 个字节中。无论出于何种原因,他可能都硬编码为 4 个字节。不客气!
  • 我明白了!另外,我自己做了一些实验,并意识到,实际上,一个非常简单的解决方案是将行 Rnd (-1) 移到 i 循环之外,我的代码将在 Mac 上运行。鉴于此,您是否仍然认为问题与您在答案中提到的 8 字节和 4 字节有关?或者更确切地说,我想我现在的问题是,为什么将Rnd (-1) 移出i 循环来修复这个“错误”?
  • @EthanMark 将Rnd -1 移到i 循环之外,与原始示例相比,第2、3、4 和5 行的值将完全不同。但是,因为您仅在获得后续行的值后才设置种子,但它们与示例中生成的原始值不同。 Mac上仍然存在该错误。如前所述,如果 4 个字节不固定,那么 Randomize i 就像 Mac 上的 Randomize 0
  • 确实很有趣!感谢您提供所有这些有见地的信息:)
【解决方案2】:

如果您阅读Randomize statement 的手册,您会发现以下内容:

要重复随机数序列,请在使用带有数字参数的 Randomize 之前立即使用负参数调用 Rnd。使用具有相同 number 值的 Randomize 不会重复前面的序列。

所以我强烈建议删除Rnd (-1),这会使其重复随机数序列。

还可以尝试从Randomize i 中删除种子并仅使用Randomize,以便计算机将系统计时器作为种子(以获得更好的随机数)。


//根据评论编辑

如果您每次都需要相同的数字,但所有行/列中的数字不同,那么您可能需要在第一个循环之前进行初始化。

Rnd -1
Randomize 1 'if you don't need the same seed everytime use Randomize without number

For i = 1 To 5

【讨论】:

  • 感谢您的回答!如前所述,我需要播种我的随机数,以便使用我的宏的任何人都会得到相同的结果。我被教导使用Rnd (-1)Randomize i 来做到这一点。如果我不这样做,那么我该如何播种我的随机数?此外,这仍然不能真正解释为什么这段代码(如我的帖子中所示)适用于 Windows 而不是我的 iMac?
  • @EthanMark 哦,我不明白你每次都想要相同的数字。然后,您可能需要将初始化放在第一个循环之前。查看我的编辑。
猜你喜欢
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多