ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MATLAB实现偏t分布:从PDF到随机数生成的完整指南

MATLAB实现偏t分布:从PDF到随机数生成的完整指南 简介偏t分布同时刻画数据的偏斜与厚尾特征在金融收益和风险度量中非常实用。这份Matlab资源围绕偏t分布提供概率密度函数、累积分布函数、分位数计算以及随机数生成的一整套代码实现适合金融风控、统计建模、量化研究等领域的开发者和科研人员直接调用或二次开发。资源包共7个文件包含6个.m函数文件和1个docx说明文档函数按功能拆分结构清晰便于理解偏t分布各统计量的算法逻辑压缩包仅26KB部署轻量。目前已有1649人学习下载代码经作者校正可稳定运行。通过dskt、pskt、qskt等功能模块可方便求出指定参数下的概率密度、累积概率、分位数并生成服从偏t分布的随机数直接服务于蒙特卡洛模拟等任务附带的文档还额外给出了无约束条件下Prim算法的Matlab实现可作为图论算法学习的对照用例。若运行环境有差异导致报错可以联系作者获取指导。1. 偏 t 分布为什么没有直接落在 MATLAB 工具箱里做金融收益拟合、水文极值分析或者可靠性试验时样本直方图经常是单峰但左右不对称尾部还比正态分布厚。用普通 t 分布能抓住厚尾却丢掉了非对称用偏正态分布能抓住偏度尾部又常常不够重。偏 t 分布skew-t同时提供位置xi、尺度omega、偏度alpha和自由度nu四个参数alpha0时退化为普通 tnu趋向无穷时退化为偏正态所以很多模型用它做标准残差的分布假设。麻烦在于 MATLAB 自带分布目录里没有这一项makedist不支持tLocationScaleDistribution也只到对称 t。实际项目里最可靠的做法是自写四个函数概率密度pdf、累积分布CDF、分位数quantiles和随机数生成。以下按 Azzalini 参数化给出一套可直接落地的 MATLAB 实现新手能照抄运行熟手可以拿参数边界和数值稳定性做参考。2. 偏 t 分布的 pdf 与 CDFAzzalini 参数化公式和 MATLAB 函数2.1 两种“偏 t”参数化接错公式会全盘错Azzalini 参数化的标准密度是f(z; alpha, nu) 2 * t_nu(z) * T_{nu1}(alpha * z * sqrt((nu1)/(nuz^2)))。其中t_nu是自由度为nu的 t 密度T_{nu1}是自由度为nu1的 t 累积分布函数。加上位置尺度后X xi omega * Z最终 PDF 外面还要除以omega。alpha为正时右尾更长alpha为负时左尾更长nu越小尾部越厚。不要被搜索结果里另一种 Hansen 偏 t 带偏。Hansen 的参数化写成nu和lambda并预先做了方差标准化常见于 GARCH 模型标准化残差它的密度公式和自由度限制通常要求nu2和 Azzalini 版并不兼容。如果直接拿错公式套进 MATLAB同一组nu数值会得到完全不同的曲线。下面参数表以 Azzalini 版本为准。参数含义取值范围典型作用xi位置参数(-Inf, Inf)控制分布中心位置omega尺度参数(0, Inf)控制整体展宽alpha偏度参数(-Inf, Inf)符号决定偏向哪一侧nu自由度(0, Inf)nu2时方差不存在表里nu1时均值不存在nu2时方差不存在这个在随机数验证时很关键。alpha0时公式里T_{nu1}(0)0.5PDF 退化为普通 tnu取很大值时分母的卡方变量趋近 1分布趋向偏正态。用退化关系做自检alpha0时stpdf应与tpdf((x-xi)/omega, nu)/omega逐点相等。2.2 stpdf向量化密度实现并备一个无工具箱版本function y stpdf(x, xi, omega, alpha, nu) % 偏t分布(Azzalini参数化)概率密度函数, 输入x可为向量 z (x - xi) / omega; term alpha .* z .* sqrt((nu 1) ./ (nu z.^2)); y 2 .* tpdf(z, nu) .* tcdf(term, nu 1) ./ omega; end这里./和.*是为了让x是列向量时逐元素计算tpdf(z,nu)返回和z同形的密度值tcdf(term,nu1)返回标尺 t 的累积概率。term里的(nu1)./(nuz.^2)是 Azzalini 密度里自由度从nu过渡到nu1的权重少这一个根号曲线在偏度大时会严重偏离真实值。omega放在最后除而不是放进z里乘是为了让密度在尺度变大时自动变矮保证积分仍为 1。如果没装 Statistics and Machine Learning Toolboxtpdf可以手写function y tpdf_manual(z, nu) y gamma((nu1)/2) ./ (sqrt(nu*pi) * gamma(nu/2)) .* ... (1 z.^2 / nu) .^ (-(nu1)/2); endgamma是 MATLAB 基础函数不需要额外工具箱。tcdf也能用betainc替换x0时T(x)1-0.5*betainc(nu/(nux^2), nu/2, 0.5)负半轴对称处理。不过大部分场景下统计工具箱是现成的所以主代码继续用tpdf和tcdf。2.3 stcdf用 integral 对密度从 -Inf 积分function p stcdf(x, xi, omega, alpha, nu) % 偏t分布累积分布函数, 对x每个元素数值积分 p arrayfun(... (x0) integral((t) stpdf(t, xi, omega, alpha, nu), ... -Inf, x0, AbsTol, 1e-10, RelTol, 1e-8), x); endarrayfun对每个x0独立做一次数值积分因此输出形状和x一致。容差上不建议把AbsTol压到 1e-12 这类水平被积函数在远离中心时接近 0积分器会在门槛附近反复缩小步长最后报 “MINIMUM STEP SIZE” 警告对分位数反解也几乎没有精度帮助。integral默认容差是AbsTol1e-10, RelTol1e-6改成RelTol1e-8是为了让后面fzero的分位数迭代更稳。容差组合常用场景备注默认AbsTol1e-10, RelTol1e-6画 CDF 曲线速度快尾部可能有微小波动AbsTol1e-10, RelTol1e-8quantiles 反解fzero每步更稳推荐手工分段积分nu2或abs(alpha)20分成[-Inf,0]与[0,x0]两段调用如果自由度特别小比如nu0.5密度在 0 附近非常陡峭分段积分能避开单次变量替换带来的振荡。第 3 章的分位数反解里CDF 只负责提供单调连续函数值精度到 1e-8 量级就足够不需要追求机器精度。3. quantiles用 fzero 反解 CDF 的 MATLAB 实现3.1 为什么不能用 icdf以及 fzero 的初始点选择MATLAB 的icdf函数按名字查内置分布偏 t 不在名单里。能直接用的方法是在任意x处算F(x)再用fzero解F(x)-p0。难点不是单调性而是初值给一个离分位数太远的初始点fzero虽然也能收敛但会多出很多次 CDF 积分而每次 CDF 积分都是一次数值积分的开销。常见做法是用同自由度的对称 t 分位数做起点x0 xi omega * tinv(p, nu)。偏度参数alpha的影响在这个起点上已经接近之后用带括号的fzero校正alpha带来的偏移。这样大多数情况下迭代 5 到 15 次 CDF 计算就能收敛。3.2 stinv自适应括号加 fzero 的完整代码function q stinv(p, xi, omega, alpha, nu) % 偏t分布分位数(quantiles)反解, p为0到1之间的概率 if omega 0 error(omega 必须大于 0); end if any(p 0 | p 1) error(p 必须在开区间 (0,1) 内); end q zeros(size(p)); for i 1:numel(p) q(i) stinv_single(p(i), xi, omega, alpha, nu); end end function x stinv_single(p, xi, omega, alpha, nu) % 单个概率值的反解: 对称t作初值, 再自动扩展括号 cdfFun (x) stcdf(x, xi, omega, alpha, nu); x0 xi omega * tinv(p, nu); f0 cdfFun(x0) - p; if abs(f0) 1e-12 x x0; return; end h max(abs(x0 - xi), omega); lo x0 - h; hi x0 h; while cdfFun(lo) - p 0 || cdfFun(hi) - p 0 h h * 2; lo x0 - h; hi x0 h; if h 1e8 * omega error(分位数搜索区间未能包住根); end end options optimset(TolX, 1e-10, Display, off); x fzero((x) cdfFun(x) - p, [lo, hi], options); endstinv是入口负责参数校验和按p的原始形状输出stinv_single处理单个概率。h的初值取max(abs(x0-xi), omega)意思是至少覆盖一个尺度范围如果上下界处 CDF 与p的大小关系不对说明分位数不在括号内每次翻倍扩展。fzero拿到一个异号区间后内部先二分再逆二次插值比单点迭代稳得多。TolX1e-10控制返回坐标精度这个值对绝大多数实际场景足够。p是行向量时zeros(size(p))保持行向量是列向量时返回列向量。while循环里每次扩展都调两次 CDF极端尾部可能会扩展到几十个omega但不会超过1e8*omega。出现这个错误时先检查p是否在开区间再检查nu是否过小最后看alpha绝对值是否过大导致初始点离真根太远。3.3 极端分位数场景下的三个经验值当p到 1e-6 或 1-1e-6 时tinv(p,nu)给出的初始点已经很远x0-xi可能超过几十个omega括号扩展要多花几次。这时有三个经验参数值得记住。第一把TolX压到 1e-12 不会让结果更准因为stcdf本身容差在 1e-8 量级继续压迭代精度没有意义。第二abs(alpha)10时分位数对alpha的敏感度下降如果只需要粗略估计可以先用alpha10近似再按delta的单调性方向微调。第三不要直接传p0或p1tinv会返回Inf整个求根过程失去意义。提示分位数反解适合一次性求几个关键点比如[0.01, 0.05, 0.5, 0.95, 0.99]。需要几千上万个随机样本时不要走这条路径用第 4 章的隐变量法生成随机数速度会快一个量级以上。4. 生成偏 t 分布随机数从偏正态到 chi-square 的隐变量法4.1 隐变量结构比逆变换法快一个量级一句话版本偏 t 随机变量可以写成两个独立随机变量的函数。先生成标准偏正态Z_SN再生成独立卡方V ~ chi2(nu)则X xi omega * Z_SN / sqrt(V / nu)服从 Azzalini 偏 t。这个结构和 t 分布“正态除以卡方”的关系一脉相承分子换成偏正态后非对称性保留分母提供厚尾。相比第 3 章的 inverse-CDF它不需要任何数值积分生成 10 万个点的耗时通常在毫秒级。偏正态本身的生成也有闭式方法Z_SN delta * abs(U0) sqrt(1-delta^2) * U1其中U0、U1独立标准正态delta alpha / sqrt(1alpha^2)。这个式子里abs(U0)是半正态提供单侧偏置两个正态分量的线性组合保证最终分布属于偏正态族。这里有一个很容易忽略的点delta的绝对值恒小于 1所以线性组合的系数始终有定义且是实数。4.2 strnd完整生成函数function r strnd(n, xi, omega, alpha, nu) % 生成n个偏t分布随机数, Azzalini参数化 delta alpha / sqrt(1 alpha^2); u0 randn(n, 1); u1 randn(n, 1); zSn delta .* abs(u0) sqrt(1 - delta.^2) .* u1; v chi2rnd(nu, n, 1); r xi omega .* zSn ./ sqrt(v / nu); enddelta的范围被压缩到(-1,1)。alpha0时delta0zSnu1分布退化为普通 talpha0时abs(u0)的正值部分经过delta加权把密度往右侧拉伸右尾变长。chi2rnd(nu,n,1)产生n个卡方样本除以自由度后开方就是 t 分布里经典的尺度因子。最后xi omega .* ...完成位置尺度变换omega必须为正。参数校验上strnd不强制nu2因为即使自由度小于 2样本仍能生成只是mean和std不再稳定。如果调用方后续要做矩估计应当在外部检查nu2。生成样本数量很大时randn(n,1)改成randn(1,n)也可以只要保持u0、u1、v形状一致即可。4.3 可视化验证直方图叠加 pdf别用 std 收尾rng(42); r strnd(1000000, 0, 1, 5, 5); histogram(r, 500, Normalization, pdf, EdgeColor, none); hold on; xx linspace(-8, 8, 500); plot(xx, stpdf(xx, 0, 1, 5, 5), r-, LineWidth, 2); hold off;rng(42)固定随机种子保证可复现直方图用 500 个 bin 保持形状细节Normalizationpdf让直方图面积归一能和 pdf 曲线直接比较。alpha5的正偏体现在右尾拉伸左尾在 -4 附近就基本贴到 0。如果发现曲线和直方图错位先查omega缩放和alpha符号再查stpdf里z.^2的写法是否保持逐元素运算。验证分布矩时nu必须足够大理论公式如下。统计量表达式xi0, omega1存在的条件均值delta * sqrt(nu/pi) * gamma((nu-1)/2) / gamma(nu/2)nu 1方差nu/(nu-2) * (1 - 2*delta^2/pi)nu 2以nu5, alpha3为例delta0.9487理论均值大约 1.48标准差大约 1.54。生成 1e6 个点后mean(r)和std(r)应该与理论值接近如果nu2std会随样本量剧烈跳动这是分布本身的性质不是代码 bug。5. 把 pdf/CDF/quantiles/随机数串成一个函数工厂5.1 skewt_factory避免每个调用都写参数四个函数分散写每次调用都要带xi, omega, alpha, nu参数一多容易写错。常见做法是用闭包做函数工厂参数只传一次function S skewt_factory(xi, omega, alpha, nu) if omega 0 error(omega 必须大于 0); end S struct(); S.pdf (x) stpdf(x, xi, omega, alpha, nu); S.cdf (x) stcdf(x, xi, omega, alpha, nu); S.inv (p) stinv(p, xi, omega, alpha, nu); S.rnd (n) strnd(n, xi, omega, alpha, nu); end用法st skewt_factory(0, 1, 3, 6); q st.inv([0.05, 0.5, 0.95]); r st.rnd(5000);调用st.pdf(x)、st.cdf(x)时不再出现那四个参数代码可读性好很多。注意struct里存函数句柄时xi等变量被快照进句柄作用域之后修改原工作区变量不会影响S这是闭包的标准行为。5.2 最容易踩的坑alpha 符号、omega 正负和分位数符号第一alpha0的右偏意味着概率质量集中在左侧众数在均值左边。画图时若看到峰偏左先确认alpha0再继续不要急着改符号。第二omega写成负数会让密度镜像翻转且不再是概率密度函数所以工厂函数里首先要校验omega0。第三金融场景计算 VaR 时通常取左尾概率p0.05左偏数据要设alpha0返回的st.inv(0.05)是负数如果把alpha设反VaR 会低估风险。提示用样本skewness(r)只能验证符号不能拿它当alpha。alpha是分布形状参数和样本三阶矩之间没有固定换算关系除非固定nu。自己验证一遍闭环st.rnd(1e5)生成样本用quantile(r, [0.05, 0.5, 0.95])与st.inv([0.05, 0.5, 0.95])对比偏差在几个抽样标准误范围内说明四个函数内部一致再把alpha0代入和tinv的结果对比应该完全重合。本文还有配套的精品资源点击获取
返回列表