2016-02-27 69 views
0

是基于以下3个步骤的算法此方法#VALUE错误:函数返回有时

1 - 生成关于[-1,1]的间隔,你将调用U1和U2

2个统一编号

2 - 计算S = U1^2 + U2^2

3 - 若S < 1正常数由U1给出*平方根(-2 LN(S)/ S),否则返回到步骤1直到S < 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

你曾经传递一个负值到日志(S )? –

+0

我不认为S会是负面的,因为S是U1和U2平方的总和,所以方块总会返回正数 – newguy

+0

右对。尝试一下,而不是像你建议的循环,然后 –

回答

1

我已经取得了一些调整到最终输出,输出不标准分布,但样品的分布,所以乘法西格玛然后加上亩。否则,该功能将不需要任何输入。

Rnd是本地VBA生成随机数,它总是落在(0,1)之内。

您可以使用GoTo而不是执行do...loop,以便您不必调用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 
+0

啊,我看到你的第二次尝试,也许'U1 = WorksheetFunction.RandBetween(-1,1)'更合适 – Rosetta

+0

感谢您的帮助,但它仍然返回#值错误。 – newguy

+0

纠正....我认为这应该工作 – Rosetta

2

KS Sheon工作流是正确的

  • WorksheetFunction.RandBetween(-1,1)返回和1

    -1之间的整数,而VBA了Rnd()函数返回0和1之间的随机双精度值0

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

我后两种解决方案(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 
+0

哈哈是的你是对的,我没有想到。顺便说一句U1 shud被允许为负数字我。即(-1,1)。 Rnd只在(0,1)范围内。 – Rosetta

+0

是递归函数更优雅。但只是不想混淆他。 – Rosetta

+0

更正U1和U2设置:添加“-1”返回-1和1之间的双打 – user3598756