函数返回#Value错误有时

时间:2016-02-27 05:01:16

标签: vba function excel-vba excel-formula excel

此方法基于以下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错误

2 个答案:

答案 0 :(得分:2)

KS Sheon工作流程是正确的

  • WorksheetFunction.RandBetween(-1,1)返回介于-1和1之间的整数

    而VBA Rnd()函数返回0到1之间的随机双重

  • VBA Log()函数实际返回自然对数

我发布了两个解决方案(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