Function sbRandGeneral(dMin As Double, dMax As Double, vXi As Variant, _
vWi As Variant, Optional dRandom As Double = 1#) As Double
'Generates a random number, General distributed.
'Do not forget to run Randomize command from a calling VBA routine before you run this function.
'[see Vose: Risk Analysis, 2nd ed., p. 116]
'Source: https://berndplumhoff.gitbook.io/sulprobil/excel/excel-vba-solutions/sbrandgeneral
'(C) (P) by Bernd Plumhoff 26-Jul-2020 PB V1.01
'Similar to @RISK's (C) RiskGeneral function.
Static bRandomized As Boolean
Dim i As Long, lWiCount As Long, lXiCount As Long
Dim dA As Double, dRand As Double, dSgn As Double
On Error GoTo ErrorLabelIsVariant
If lWiCount <> lXiCount Then
sbRandGeneral = CVErr(xlErrValue)
ReDim dX(0 To lXiCount + 1) As Double
ReDim dW(0 To lWiCount + 1) As Double
For i = 0 To UBound(dX) - 1
If dX(i) >= dX(i + 1) Or dW(i) < 0# Then
sbRandGeneral = CVErr(xlErrValue)
dA = dA + (dX(i + 1) - dX(i)) * (dW(i + 1) + dW(i)) / 2#
'Normalise weights to set area to 1
For i = 1 To UBound(dW) - 1
ReDim dF(0 To UBound(dX)) As Double
'Calculate border points of value ranges for
'cumulative inverse function
For i = 0 To UBound(dX) - 1
dA = dA + (dX(i + 1) - dX(i)) * (dW(i + 1) + dW(i)) / 2#
dSgn = Sgn(dW(i) - dW(i - 1))
sbRandGeneral = dX(i - 1) + (dRand - dF(i - 1)) / _
(dF(i) - dF(i - 1)) * (dX(i) - dX(i - 1))
sbRandGeneral = dX(i - 1) + _
dSgn * Sqr((dRand - dF(i - 1)) * _
2# * (dX(i) - dX(i - 1)) / (dW(i) - dW(i - 1)) + _
(dW(i - 1) * (dX(i) - dX(i - 1)) / _
(dW(i) - dW(i - 1))) ^ 2#) - _
dW(i - 1) * (dX(i) - dX(i - 1)) / (dW(i) - dW(i - 1))
lXiCount = UBound(vXi) - 1
lWiCount = UBound(vWi) - 1
Resume ErrorLabelWasVariant