通过 Cholesky 与卡方标度生成 t-Copula 相关样本
t-Copula Correlated Samples via Cholesky and Chi-Square Scaling
开始编码实现 solution(z_independent: list[list[float]], chi_sq_samples: list[float], nu: int, correlation: list[list[float]]) -> list[list[float]]。某多资产风险引擎需要在给定相关矩阵 correlation 与自由度 nu 下抽多元 Student-t 样本,以捕获 Gaussian copula 漏掉的尾部依赖。教科书做法:(1)抽 S 个 D 维独立 N(0, 1) 向量;(2)抽 S 个独立 chi-square(nu) 标量;(3)对正态做 Cholesky 变换以施加相关;(4)逐样本乘 sqrt(nu / chi_sq) 把边际从正态转成 t。第 1、2 步由调用方完成(作为 z_independent 与 chi_sq_samples 传入),本函数只做确定性的"线性代数 + 标度"核——剥离出来便于无随机源单测。对每个样本 s in 0..S-1,返回 t_corr[s] = (L @ z_independent[s]) * sqrt(nu / chi_sq_samples[s]),其中 L 是 correlation 的下三角 Cholesky 因子(满足 L @ L.T == correlation)。
三个易错点。标度方向:sqrt(nu / chi_sq),不是 sqrt(chi_sq / nu)。每样本标量而非全局也非逐维:每个样本有自己的标度标量统一乘到该样本所有 D 维。Cholesky 的细节:下 vs 上三角、mat-vec 方向 L @ z 而非 z @ L、内积索引含对角——与 Gaussian-copula 姊妹题相同。
例
solution([[1.0, 1.0]], [1.0], 4, [[1.0, 0.5], [0.5, 1.0]]) 返回 [[2.0, 2.732050807568877]]。逐步:Cholesky 给出 L = [[1, 0], [0.5, sqrt(0.75)]],所以相关正态 Z_corr[0] = [1.0, 0.5 + sqrt(0.75)] = [1.0, 1.3660254...]。逐样本标度 sqrt(nu / chi_sq) = sqrt(4 / 1) = 2.0。相乘:t_corr[0] = [2.0, 2 * 1.3660254...] = [2.0, 2.7320508...]。输出每个边际是自由度 4 的 t 分布,相关结构 0.5。
为什么是 t-copula 而非 Gaussian copula
Gaussian copula 能施加目标相关,但其联合尾衰减太快:极端联合下移(例如所有资产同时跌 5+ 个标准差)在 Gaussian copula 中指数级稀有。压力时段实测的多资产收益数据通常显示出比 Gaussian 假设厚得多的联合尾。t-copula 通过引入"逐样本的同一卡方标度"升级构造:同一卡方标量同时乘到该样本的每一维,意味着整个向量被一起重新缩放。当 chi_sq 较小时(在自由度 nu 较小的卡方下并不罕见),该样本的所有分量按比例同时爆发——这正是联合极端共振事件。数学上,这给出非零的尾部依赖:lim_{q -> 1} P(U_1 > q | U_2 > q) > 0 在 t-copula 下成立,而 Gaussian 下 = 0。nu 越小尾越厚(nu = 1 类柯西,nu -> infinity 退化为 Gaussian)。
算法
期望算法 O(D^3 + S * D^2):Cholesky 是一次性的三次步;每个样本一次下三角 mat-vec(O(D^2))加一次标量乘(O(D))。D <= 6、S <= 30 时总运算量 ~6000 基本操作。具体步骤:
- Cholesky:构造下三角
L,使L @ L.T == correlation。标准递推:
L[i][j] = (correlation[i][j] - sum_{k<j} L[i][k] * L[j][k]) / L[j][j],j < iL[i][i] = sqrt(correlation[i][i] - sum_{k<i} L[i][k]^2)L[i][j] = 0,j > i
- 逐样本循环,
s in 0..S-1:
- 算相关正态
Z_corr[s][i] = sum_{k=0..i} L[i][k] * z_independent[s][k](下三角 mat-vec,k 含对角)。 - 算标量
scale_s = sqrt(nu / chi_sq_samples[s])。 - 输出
t_corr[s][i] = Z_corr[s][i] * scale_s。
- 返回
t_corr。
需要明确的细节:S = 0 返回 []。D = 1:L = [[1]],输出 z[s][0] * sqrt(nu / chi_sq[s])。Identity 相关矩阵:L = I,输出逐维解耦。chi-square 输入由调用方预校验为严格正(>= 0.001),所以参考实现不需要处理除零或负数开方。标度是每样本一个标量,在该样本所有 D 维上统一乘。
实现细节由 stubs/stub.py 提供。
实践背景
某多资产结构化产品台跑蒙特卡洛压力测试,监测股票、利率、FX、信用因子的联合极端共振。其原 Gaussian-copula 模拟器低估了联合尾风险:在观测到的压力时段,资产相关跳升,多个因子同时爆发,但 Gaussian copula 几乎不分配概率到该联合尾。该台升级到 t-copula,nu 由历史联合尾行为标定(股票-利率-FX 篮子的 nu 通常在 4 到 10 之间,信用更低)。模块化管线把确定性的线性代数与标度核(本函数)从上游 RNG(由可种子 PRNG 层产生的独立正态 + 独立卡方)和下游边际映射(另一函数把 t 分布分量经逆边际 CDF 映射到经验边际目标)中分离出来。剥离确定性核便于按解析期望做单元测试、模拟器升级时的回归对比,以及只通过加一个 chi_sq_samples 参数和 nu 参数把 Gaussian copula 切换到 t-copula——管线其余部分(采样、边际映射、估值)不变。
约束条件
- 1 <= D == len(correlation) <= 6(相关矩阵维度)
- 0 <= S == len(z_independent) == len(chi_sq_samples) <= 30(独立样本数)
- 每个 z_independent[s] 是长度为 D 的有限浮点列表,|z| <= 5.0
- 每个 chi_sq_samples[s] 是严格正的浮点,落在 [0.001, 100]
- 1 <= nu <= 100,整数(卡方自由度)
- correlation 严格对称(correlation[i][j] == correlation[j][i] 完全相等),correlation[i][i] == 1.0,非对角在 [-1, 1],且严格正定(调用方负责)
- 输出为形状 (S, D) 的 list[list[float]];每项按 rel_tol=1e-9、abs_tol=1e-9 比对;行序与输入一致
样例
Case 1 · statement-example: D=2 rho=0.5 nu=4 chi=1 yields [[2, 2.732]]
输入: [[[1,1]],[1],4,[[1,0.5],[0.5,1]]]
期望: [[2,2.732050807568877]]
L=[[1,0],[0.5,sqrt(0.75)]]; Z_corr=[1, 1.366]; scale=sqrt(4/1)=2; t_corr=[2, 2.732]。
Case 2 · visible: D=2 rho=0.5 nu=4 chi=4 scale=1 Cholesky-only
输入: [[[1,1]],[4],4,[[1,0.5],[0.5,1]]]
期望: [[1,1.3660254037844386]]
scale=sqrt(4/4)=1;t_corr 即 Z_corr=[1, 1.366]。
Case 3 · visible: identity D=3 nu=10 chi=10 passes z through
输入: [[[0.5,-0.3,1.2]],[10],10,[[1,0,0],[0,1,0],[0,0,1]]]
期望: [[0.5,-0.3,1.2]]
L=I;scale=sqrt(10/10)=1;输出等于输入。
Case 4 · visible: D=1 single sample nu=5 chi=5 scale=1
输入: [[[2]],[5],5,[[1]]]
期望: [[2]]
D=1, L=[[1]],scale=1,输出=[2]。
Case 5 · visible: S=0 returns empty list
输入: [[],[],4,[[1,0.3],[0.3,1]]]
期望: []
S=0:返回 []。
Case 6 · visible: D=2 rho=-0.6 nu=8 chi=2 scale=2
输入: [[[3,2]],[2],8,[[1,-0.6],[-0.6,1]]]
期望: [[6,-0.39999999999999947]]
L=[[1,0],[-0.6,0.8]];Z_corr=[3,-0.2];scale=sqrt(8/2)=2;t=[6,-0.4]。
Case 7 · visible: nu=1 small chi-sq large scaling
输入: [[[1,0]],[0.04],1,[[1,0.5],[0.5,1]]]
期望: [[5,2.5]]
scale=sqrt(1/0.04)=5;Z_corr=[1,0.5];t=[5,2.5]。
Case 8 · visible: nu=100 chi=100 scale=1 acts as Gaussian copula
输入: [[[1,1]],[100],100,[[1,0.5],[0.5,1]]]
期望: [[1,1.3660254037844386]]
nu=100, chi=100,scale=1,输出与 Gaussian copula 同。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: D=2 rho=0.5 nu=4 chi=1 yields [[2, 2.732]]
输入: [[[1,1]],[1],4,[[1,0.5],[0.5,1]]]
期望: [[2,2.732050807568877]]
L=[[1,0],[0.5,sqrt(0.75)]]; Z_corr=[1, 1.366]; scale=sqrt(4/1)=2; t_corr=[2, 2.732]。
Case 2 · visible: D=2 rho=0.5 nu=4 chi=4 scale=1 Cholesky-only
输入: [[[1,1]],[4],4,[[1,0.5],[0.5,1]]]
期望: [[1,1.3660254037844386]]
scale=sqrt(4/4)=1;t_corr 即 Z_corr=[1, 1.366]。
Case 3 · visible: identity D=3 nu=10 chi=10 passes z through
输入: [[[0.5,-0.3,1.2]],[10],10,[[1,0,0],[0,1,0],[0,0,1]]]
期望: [[0.5,-0.3,1.2]]
L=I;scale=sqrt(10/10)=1;输出等于输入。
Case 4 · visible: D=1 single sample nu=5 chi=5 scale=1
输入: [[[2]],[5],5,[[1]]]
期望: [[2]]
D=1, L=[[1]],scale=1,输出=[2]。
Case 5 · visible: S=0 returns empty list
输入: [[],[],4,[[1,0.3],[0.3,1]]]
期望: []
S=0:返回 []。
Case 6 · visible: D=2 rho=-0.6 nu=8 chi=2 scale=2
输入: [[[3,2]],[2],8,[[1,-0.6],[-0.6,1]]]
期望: [[6,-0.39999999999999947]]
L=[[1,0],[-0.6,0.8]];Z_corr=[3,-0.2];scale=sqrt(8/2)=2;t=[6,-0.4]。
Case 7 · visible: nu=1 small chi-sq large scaling
输入: [[[1,0]],[0.04],1,[[1,0.5],[0.5,1]]]
期望: [[5,2.5]]
scale=sqrt(1/0.04)=5;Z_corr=[1,0.5];t=[5,2.5]。
Case 8 · visible: nu=100 chi=100 scale=1 acts as Gaussian copula
输入: [[[1,1]],[100],100,[[1,0.5],[0.5,1]]]
期望: [[1,1.3660254037844386]]
nu=100, chi=100,scale=1,输出与 Gaussian copula 同。