2016-02-27 66 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