首页 > 解决方案 > 为什么我的随机数在 Excel 中没有相应变化?

问题描述

我在下面有一些代码可以在 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 中是否有任何设置阻止我的代码正常运行?

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

标签: excelvbamacosrandomrandom-seed

解决方案


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

首先,如果我们替换行:

Randomize i

和:

Randomize 0

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

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

  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、声明了所有变量并缩进了代码。


推荐阅读