此方法基于以下3步算法:
1 - 在[-1,1]区间生成两个统一数字,您将调用U1和U2
2 - 计算S = U1 ^ 2 + U2 ^ 2
3 - 如果S&lt;在图1中,正常数由U1 *平方根(-2 ln(S)/ S)给出,否则返回步骤1直到S <1。 1。
在VB中编写此函数并将其命名为BoxMuller。
这是我根据上述步骤编写的函数我不确定它是否正确,因为有时会返回#Value错误
我将以下值传递给函数=BoxMuller(Rand(),Rand())
Function BoxMuller(U1 As Double, U2 As Double) As Double
Dim S As Double
Do
U1 = WorksheetFunction.NormInv(U1, 0, 1)
U2 = WorksheetFunction.NormInv(U2, 0, 1)
S = U1 * U1 + U2 * U2
If S < 1 Then
BoxMuller = U1 * Sqr(-2 * Log(S) / S)
Exit Function
End If
Loop Until S < 1
End Function
是Loop Until S < 1
条件,因为我认为这可能是错误的真正原因。
还尝试了以下内容:
Function BoxMuller() As Double
Dim S As Double
Dim U1 As Double
Dim U2 As Double
Do
U1 = WorksheetFunction.RandBetween(-1, 1)
U2 = WorksheetFunction.RandBetween(-1, 1)
S = U1 * U1 + U2 * U2
If S < 1 Then
BoxMuller = U1 * Sqr(-2 * Log(S) / S)
Exit Function
End If
Loop
End Function
并且被称为=BoxMuller()
仍然是#Value错误
答案 0 :(得分:2)
KS Sheon工作流程是正确的
但
WorksheetFunction.RandBetween(-1,1)返回介于-1和1之间的整数
而VBA Rnd()函数返回0到1之间的随机双重
我发布了两个解决方案(BoxMuller1和BoxMuller2),与上面的内容一样,只是编码风格不同而且都使用递归调用
Function BoxMuller1(mu As Double, sigma As Double) As Double
Application.Volatile
Dim U1 As Double, U2 As Double, S As Double
Do While GetS(Rnd, Rnd, U1, U2, S) >=1
Randomize
Loop
BoxMuller1 = U1 * Sqr(-2 * Log(S) / S) * sigma + mu
End Function
Function GetS(Rnd1 As Double, Rnd2 As Double, U1 As Double, U2 As Double, S As Double) As Double
U1 = 2*Rnd1 - 1
U2 = 2*Rnd2 - 1
S = U1 * U1 + U2 * U2
GetS = S
End Function
Function BoxMuller2(mu As Double, sigma As Double) As Double
Application.Volatile
Dim U1 As Double, U2 As Double, S As Double
Randomize
U1 = 2*Rnd -1
U2 = 2*Rnd -1
S = U1 * U1 + U2 * U2
If S >= 1 Then
BoxMuller2 = BoxMuller2(mu, sigma)
Else
BoxMuller2 = U1 * Sqr(-2 * Log(S) / S) * sigma + mu
End If
End Function
答案 1 :(得分:1)
我已经对最终输出做了一些调整,输出不是标准分布而是样本分布,所以乘以sigma然后再加mu。否则该功能不需要任何输入。
Rnd
是生成随机数的原生VBA,它始终位于(0,1)内。
您可以使用do...loop
而不是GoTo
,这样您就不必调用exit function
来结束循环。
application.volatile
将确保每次按F9时重新计算该功能。如果你不这样做,请删除它。
Function BoxMuller(mu As Double, sigma As Double) As Double
Application.Volatile
Dim U1 As Double, U2 As Double, S As Double
ReCalc:
Randomize
'U1 = Rnd 'this is not correct for the function, leaving it here for reference.
'U2 = Rnd
'U1 = WorksheetFunction.RandBetween(-1, 1) 'this is wrong too, RandBetween only returns interger
'U2 = WorksheetFunction.RandBetween(-1, 1)
U1 = Rnd * 2 - 1
U2 = Rnd 'the BoxMuller formula don't require U2 to be negative.
S = U1 * U1 + U2 * U2
If S < 1 Then
BoxMuller = U1 * Sqr(-2 * (Log(S) / S) * sigma + mu
Else
GoTo ReCalc
End If
End Function