若是未作特別說明,文中的程序都是 Python3 代碼。算法
載入模塊dom
import QuantLib as ql import scipy print(ql.__version__)
1.12
隨機模擬一般從產生均勻分佈的隨機數開始。假設 \(X \sim U [0, 1]\) 是均勻分佈的隨機變量。任意分佈的隨機數一般須要對 \(X\) 施加某種變換獲得,通常狀況下是用累積分佈函數的逆函數 \(F^{−1}\),\(F^{−1}(X)\) 的分佈就是 \(F\)。其餘的變換算法可能不須要 \(F^{−1}\),好比用於生成正態分佈的 Box Muller 變換算法。函數
均勻分佈的隨機數發生器主要分兩種:工具
quantlib-python 提供瞭如下三種均勻分佈的(僞)隨機數發生器:spa
KnuthUniformRng
,高德納(Knuth)算法LecuyerUniformRng
,L'Ecuyer 算法MersenneTwisterUniformRng
,著名的梅森旋轉(Mersenne-Twister)算法隨機數發生器的構造函數,code
Rng(seed)
其中orm
seed
,整數,默認值是 0,做爲種子用於初始化相應的肯定性序列;隨機數發生器的成員函數:對象
next()
:返回一個 SampleNumber
對象,做爲模擬的結果。r = rng.next() v = r.value(r)
用戶經過反覆調用成員函數 next()
得到一連串的隨機數,須要注意的是 r
的類型是 SampleNumber
,須要調用 value()
獲得對應的浮點數。ip
例子 1,
def testingRandomNumbers1(): seed = 1 unifMt = ql.MersenneTwisterUniformRng(seed) unifLec = ql.LecuyerUniformRng(seed) unifKnuth = ql.KnuthUniformRng(seed) print('{0:<25}{1:<25}{2:<25}'.format( 'Mersenne Twister', 'Lecuyer', 'Knut')) for i in range(10): print('{0:<25}{1:<25}{2:<25}'.format( unifMt.next().value(), unifLec.next().value(), unifKnuth.next().value())) testingRandomNumbers1()
Mersenne Twister Lecuyer Knut 0.41702199855353683 0.2853808990946861 0.4788952510312594 0.9971848082495853 0.2533581892659171 0.7694635535665499 0.7203244894044474 0.09346853100919404 0.47721285286866455 0.9325573613168672 0.6084968907396475 0.15752737762851 0.00011438119690865278 0.90342026007861 0.6065713927733087
隨機模擬中最多見的分佈是正態分佈,quantlib-python 提供的正態分佈隨機數發生器有 4 類:
CentralLimitABCGaussianRng
BoxMullerABCGaussianRng
MoroInvCumulativeABCGaussianRng
InvCumulativeABCGaussianRng
其中 ABC
特指一種均勻隨機數發生器。
具體來說 4 類發生器分爲 12 種:
CentralLimitLecuyerGaussianRng
CentralLimitKnuthGaussianRng
CentralLimitMersenneTwisterGaussianRng
BoxMullerLecuyerGaussianRng
BoxMullerKnuthGaussianRng
BoxMullerMersenneTwisterGaussianRng
MoroInvCumulativeLecuyerGaussianRng
MoroInvCumulativeKnuthGaussianRng
MoroInvCumulativeMersenneTwisterGaussianRng
InvCumulativeLecuyerGaussianRng
InvCumulativeKnuthGaussianRng
InvCumulativeMersenneTwisterGaussianRng
隨機數發生器的構造函數:
rng = Rng(seed) grng = Gaussianrng(rng)
正態分佈隨機數發生器接受一個對應的均勻分佈隨機數發生器做爲源,以 BoxMullerMersenneTwisterGaussianRng
爲例,須要配置一個 MersenneTwisterUniformRng
對象做爲隨機數的源,使用經典的 Box-Muller 算法獲得正態分佈隨機數。
例子 2,
def testingRandomNumbers2(): seed = 12324 unifMt = ql.MersenneTwisterUniformRng(seed) bmGauss = ql.BoxMullerMersenneTwisterGaussianRng(unifMt) for i in range(5): print(bmGauss.next().value()) testingRandomNumbers2()
-1.1756781173398896 0.14110041851886157 1.569582906805544 -0.026736779238941934 -0.8220676600472409
相較於以前描述的「僞」隨機數,隨機模擬中另外一類重要的隨機數成爲「擬」隨機數,也稱爲低誤差序列。由於收斂性更好,擬隨機數一般用於高維隨機變量的模擬。quantlib-python 提供的擬隨機數有兩類,
HaltonRsg
: Halton 序列SobolRsg
: Sobol 序列HaltonRsg
HaltonRsg
的構造函數,
HaltonRsg(dimensionality, seed, randomStart, randomShift)
其中,
dimensionality
:整數,設置維度;seed
,整數,默認值是 0,做爲種子用於初始化相應的肯定性序列;randomStart
:布爾值,默認是 True
,是否隨機開始;randomShift
:布爾值,默認是 False
,是否隨機平移。HaltonRsg
的成員函數,
nextSequence()
:返回一個 SampleRealVector
對象,做爲模擬的結果;lastSequence()
:返回一個 SampleRealVector
對象,做爲上一個模擬的結果;dimension()
:返回維度。SobolRsg
SobolRsg
的構造函數,
SobolRsg(dimensionality, seed, directionIntegers=Jaeckel)
其中,
dimensionality
:整數,設置維度;seed
,整數,默認值是 0,做爲種子用於初始化相應的肯定性序列;directionIntegers
,quantlib-python 的內置變量,默認值是 SobolRsg.Jaeckel
,用於 Sobol 序列的初始化。SobolRsg
的成員函數,
nextSequence()
:返回一個 SampleRealVector
對象,做爲模擬的結果;lastSequence()
:返回一個 SampleRealVector
對象,做爲上一個模擬的結果;dimension()
:返回維度。skipTo(n)
:n
是整數,跳轉到抽樣結果的第 n 個維度;nextInt32Sequence()
:返回一個 IntVector
對象。例子 3,
def testingRandomNumbers4(): dim = 5 haltonGen = ql.HaltonRsg(dim) sobolGen = ql.SobolRsg(dim) sampleHalton = haltonGen.nextSequence().value() sampleSobol = sobolGen.nextSequence().value() print('{0:<25}{1:<25}'.format( 'Halton', 'Sobol')) for i in range(dim): print('{0:<25}{1:<25}'.format( sampleHalton[i], sampleSobol[i])) testingRandomNumbers4()
Halton Sobol 0.04081786540336907 0.5 0.8535710143553551 0.5 0.69400573329408 0.5 0.818105927979147 0.5 0.878826694887864 0.5
最後用一個例子比較兩類隨機數的收斂性,分別產生正態分佈的僞隨機數和擬隨機數,計算分佈的四個統計指標:
def testingRandomNumbers5(): sobolGen = ql.SobolRsg(1) seed = 12324 unifMt = ql.MersenneTwisterUniformRng(seed) bmGauss = ql.BoxMullerMersenneTwisterGaussianRng(unifMt) boxMullerStat = ql.IncrementalStatistics() sobolStat = ql.IncrementalStatistics() invGauss = ql.MoroInverseCumulativeNormal() numSim = 10000 for j in range(numSim): boxMullerStat.add(bmGauss.next().value()) currSobolNum = sobolGen.nextSequence().value()[0] sobolStat.add(invGauss(currSobolNum)) stats = { "BoxMuller Mean:": boxMullerStat.mean(), "Sobol Mean:": sobolStat.mean(), "BoxMuller Var:": boxMullerStat.variance(), "Sobol Var:": sobolStat.variance(), "BoxMuller Skew:": boxMullerStat.skewness(), "Sobol Skew:": sobolStat.skewness(), "BoxMuller Kurtosis:": boxMullerStat.kurtosis(), "Sobol Kurtosis:": sobolStat.kurtosis()} for k, v in stats.items(): print('{0:>25}{1:>30}'.format(k, v)) testingRandomNumbers5()
BoxMuller Mean: 0.005966482725988245 Sobol Mean: -0.0002364019095203635 BoxMuller Var: 1.0166044844467006 Sobol Var: 0.9986010126883317 BoxMuller Skew: 0.02100635339070779 Sobol Skew: -7.740573185322994e-05 BoxMuller Kurtosis: -0.0340476839897507 Sobol Kurtosis: -0.020768126049145776
直觀上看 Sobol 序列的結果更加接近理論值,這證實使用擬隨機數作模擬的收斂速度更好。