Abstract
Please notice that sbRandTrigen needs and calls sbRandTriang.
Documentation
The documentation for this application you can open and read here:
Please read my Disclaimer.
071228_PB_02_Trigen_Doc.pdf [38 KB PDF file, download, open, and use at your own risk]
Appendix – sbRandTrigen Code
Please read my Disclaimer.
Option Explicit
Function sbRandTrigen(dBottom As Double, dMode As Double, _
dTop As Double, dBottomPerc As Double, _
dTopPerc As Double, Optional dRandom = 1#) As Double
'Generates dMin random number, Triang distributed
'with given first and last decile
'[see Vose: Risk Analysis, 2nd ed., p. 129]
'Source (EN): http://www.sulprobil.de/sbrandgeneral_en/
'Source (DE): http://www.berndplumhoff.de/sbrandgeneral_de/
'(C) (P) by Bernd Plumhoff 19-Nov-2011 PB V0.32
'Similar to @RISK's (C) RiskTrigen function.
'sbRandTrigen(bottom, mode, top, bottom percentile, top percentile)
'specifies a triangular distribution with three points — one
'at the mode and two at the specified bottom and top percentiles.
'The bottom percentile and top percentile are values between
'0 and 100. Each percentile value gives the percentile of the
'total area under the triangle that is on the left side of the
'given point.
'Example:
'sbRandTrigen(1,8,10,20,95) will call
'sbRandTriang(-6.13212712795534, 8, 11.8648937411641).
'Please ensure that you execute Randomize before you call
'this function for the first time.
Static dBottomLast As Double
Static dModeLast As Double
Static dTopLast As Double
Static dBottomPercLast As Double
Static dTopPercLast As Double
Static dMin As Double
Static dMax As Double
Dim dMaxNew As Double
Dim da0 As Double, da1 As Double, da2 As Double
Dim da3 As Double, da4 As Double
Dim dfe As Double, df1e As Double
Dim dBottomPerc2 As Double, dTopPerc2 As Double
Dim i As Long
If dBottom = dBottomLast And dMode = dModeLast And dTop = dTopLast _
And dBottomPerc = dBottomPercLast And dTopPerc = dTopPercLast _
And Not IsError(dMin) Then
sbRandTrigen = sbRandTriang(dMin, dMode, dMax, dRandom)
Exit Function
End If
dBottomLast = dBottom
dModeLast = dMode
dTopLast = dTop
dBottomPercLast = dBottomPerc
dTopPercLast = dTopPerc
dBottomPerc2 = dBottomPerc / 100#
dTopPerc2 = 1# - dTopPerc / 100#
If dMode <= dBottom Or dTop <= dMode Then
dMin = CVErr(xlErrValue) 'Trigger rerun next time
sbRandTrigen = CVErr(xlErrValue)
Exit Function
End If
If dBottomPerc2 < 0# Or dTopPerc2 < 0# Then
dMin = CVErr(xlErrDiv0) 'Trigger rerun next time
sbRandTrigen = CVErr(xlErrValue)
Exit Function
End If
If dTopPerc2 = 0# Then
If dBottomPerc2 = 0# Then
sbRandTrigen = sbRandTriang(dBottom, dMode, dTop, dRandom)
Exit Function
End If
sbRandTrigen = sbRandTrigen(dBottom, dMode, dTop, dBottomPerc2, dTopPerc2)
Exit Function
End If
da4 = dBottomPerc2 * dTopPerc2 - dBottomPerc2 + 1# - 2# * dTopPerc2 + dTopPerc2 ^ 2#
da3 = -2# * dBottomPerc2 * dTopPerc2 * dTop - 2# * dBottomPerc2 * dTopPerc2 * dMode - _
4# * dTop + 4# * dBottomPerc2 * dTop + 2# * dTopPerc2 * dMode + 4# * dTopPerc2 * _
dTop + 2# * dTopPerc2 * dBottom - 2# * dTopPerc2 ^ 2# * dMode - _
2# * dTopPerc2 ^ 2# * dBottom
da2 = dBottomPerc2 * dTopPerc2 * dTop ^ 2# + 4# * dBottomPerc2 * dTopPerc2 * dMode * _
dTop + dBottomPerc2 * dTopPerc2 * dMode ^ 2# - 6# * dBottomPerc2 * dTop ^ 2# + _
6# * dTop ^ 2# - 4# * dTopPerc2 * dMode * dTop - 2# * dTopPerc2 * dTop ^ 2# - 2# * _
dTopPerc2 * dBottom * dMode - 4# * dTopPerc2 * dBottom * dTop + dTopPerc2 ^ 2# * _
dMode ^ 2# + 4# * dTopPerc2 ^ 2# * dBottom * dMode + dTopPerc2 ^ 2# * dBottom ^ 2#
da1 = -2# * dBottomPerc2 * dTopPerc2 * dMode * dTop ^ 2# - 2# * dBottomPerc2 * dTopPerc2 * _
dMode ^ 2# * dTop + 4# * dTop ^ 3# * dBottomPerc2 - 4# * dTop ^ 3# + 2# * dTopPerc2 * _
dMode * dTop ^ 2# + 4# * dTopPerc2 * dBottom * dMode * dTop + 2# * dTopPerc2 * _
dBottom * dTop ^ 2# - 2# * dTopPerc2 ^ 2# * dBottom * dMode ^ 2# - 2# * _
dTopPerc2 ^ 2# * dBottom ^ 2# * dMode
da0 = dBottomPerc2 * dTopPerc2 * dMode ^ 2# * dTop ^ 2# - dBottomPerc2 * dTop ^ 4# + dTop ^ 4# - _
2# * dTopPerc2 * dBottom * dMode * dTop ^ 2# + dTopPerc2 ^ 2# * dBottom ^ 2# * dMode ^ 2#
dMax = dTop + (dTop - dMode) / (1# - dTopPerc2) ^ 2#
'Newton iteration
Do While Abs(dMaxNew - dMax) > 0.000000000001
i = i + 1
If i > 30 Then
If Abs(dfe) > 0.000000000001 Then
dMin = CVErr(xlErrDiv0) 'Trigger rerun next time
sbRandTrigen = CVErr(xlErrValue)
Exit Function
Else
Exit Do
End If
End If
dMaxNew = dMax
dfe = da4 * dMaxNew ^ 4# + da3 * dMaxNew ^ 3# + da2 * dMaxNew ^ 2# + da1 * dMaxNew + da0
df1e = 4# * da4 * dMaxNew ^ 3# + 3# * da3# * dMaxNew ^ 2# + 2# * da2 * dMaxNew + da1
dMax = dMax - dfe / df1e
Loop
dMin = dMax - (dMax - dTop) ^ 2# / dTopPerc2 / (dMax - dMode)
sbRandTrigen = sbRandTriang(dMin, dMode, dMax, dRandom)
End Function
Download
Please read my Disclaimer.
sbRandTrigen.xlsm [59 KB Excel file, open and use at your own risk]