明确目标:我们要生成什么?
根据物理海洋学的观测,海面高度通常被认为是一个高斯随机场。这意味着,如果我们想在频域中表达这个海面,我们需要生成一系列复数 $H_0$ 表示振幅,并且这些复数 $H_0$ 必须满足两个条件:
- 能量匹配:这些随机振幅的能量必须符合能量谱 $S(\mathbf{k})$。
- 随机性:它们必须符合高斯分布(正态分布)。
推导符合条件的 $H_0$
处理第一个条件: 在信号处理中,频域中某个波数 $k$ 的复数振幅 $H_0(\mathbf{k})$ 的能量,等于它的模的平方的期望值,即
$$ \mathbb{E}\left[|H_0(\mathbf{k})|^2\right] $$根据第一个条件,我们需要让这个期望值(能量)等于能量谱 $S(\mathbf{k})$
$$ \mathbb{E}\left[|H_0(\mathbf{k})|^2\right] = S(\mathbf{k}) $$处理第二个条件: 现在引入一个复高斯随机数 $Z$ ,构造出符合正太分布的 $H_0(\mathbf{k})$ 为
$$ H_0(\mathbf{k}) = Z \sqrt{S(\mathbf{k})} $$计算期望值,
$$ \mathbb{E}\left[|H_0(\mathbf{k})|^2\right] = \mathbb{E}\left[| Z \sqrt{S(\mathbf{k})} |^2\right] = \mathbb{E}\left[|Z|^2\right] S(\mathbf{k}) $$再根据第一个条件,
$$ \mathbb{E}\left[|H_0(\mathbf{k})|^2\right] = \mathbb{E}\left[|Z|^2\right] S(\mathbf{k}) = S(\mathbf{k}) $$由此,我们可知引入的 $Z$ 的期望值 $\mathbb{E}\left[|Z|^2\right]$ 必须为 1。
最终符合条件的 $H_0$ 为
$$ H_0(\mathbf{k}) = Z \sqrt{S(\mathbf{k})} \quad,\text{其中} \mathbb{E}\left[|Z|^2\right] = 1 $$构造复高斯随机数
我们通过计算机随机生成两个独立的、服从标准正态分布(均值为 0,方差为 1)的实数,记为 $\xi_r$ 和 $\xi_i$。即:
$$\mathbb{E}[\xi_r^2] = 1, \quad \mathbb{E}[\xi_i^2] = 1$$我们将它们组合成一个复数:
$$ Z=\xi_r + i\xi_i $$它的期望值是:
$$ \mathbb{E}\left[|Z|^2\right]=\mathbb{E}[\xi_r^2 + \xi_i^2] = \mathbb{E}[\xi_r^2] + \mathbb{E}[\xi_i^2] = 1 + 1 = 2 $$这与我们的条件不符,为了把期望值从 2 降到 1,我们需要在前面乘以 $\frac{1}{\sqrt{2}}$ :
$$Z = \frac{1}{\sqrt{2}} \left(\xi_r + i\xi_i\right)$$此时
$$ \mathbb{E}[|Z|^2] = 1 $$这就得到了一个纯粹的、没有任何物理偏好的“白噪声”振幅。
代入式子,我们得到 $H_0$:
$$ H_0(\mathbf{k}) = \frac{1}{\sqrt{2}} \left(\xi_r + i\xi_i\right) \sqrt{S(\mathbf{k})} $$离散化修复
在物理学中,$S(\mathbf{k})$ 的严谨叫法是功率谱密度 (Power Spectral Density, PSD)。 注意“密度”这两个字。就像物理上的质量等于密度乘以体积($m = \rho \times V$)一样,在波数域中,某个特定波数的绝对能量,等于能量密度乘以它所占据的面积。
- 在连续的数学推导中,总能量是积分:$E_{total} = \iint S(\mathbf{k}) dk_x dk_y$
- 在计算机中,我们只能用离散的网格点来近似。 所以,在计算机中, $S(\mathbf{k})$ 代表的是当前网格点的能量密度,而一个离散网格点模拟的是物理现实中的一块连续面,因此,我们需要计算出一个网格点所代表的物理面积大小,才能计算出该网格点的绝对能量。
根据
,我们得到(根据构建坐标的方式可能有所不同): $$ \Delta\mathbf{k}=\mathbf{k}_{n+1} - \mathbf{k}_{n} = \frac{2 \pi}{L} $$其中 $L$ 为所模拟的物理区域大小。 则一个离散网格点所代表的物理面积大小为,
$$ S = \Delta\mathbf{k}^2 $$代入式子,我们得 最终的 $H_0$:
$$ H_0(\mathbf{k}) = \frac{1}{\sqrt{2}} \left(\xi_r + i\xi_i\right) \sqrt{S(\mathbf{k}) \Delta\mathbf{k}^2 } $$示例代码
// 简单的伪随机数哈希函数 (Wang Hash)
uint WangHash(uint seed)
{
seed = (seed ^ 61) ^ (seed >> 16);
seed *= 9;
seed = seed ^ (seed >> 4);
seed *= 0x27d4eb2d;
seed = seed ^ (seed >> 15);
return seed;
}
// 获取 0 到 1 之间的均匀分布随机数
float RandomFloat(inout uint seed)
{
seed = WangHash(seed);
return float(seed) * (1.0 / 4294967296.0);
}
// Box-Muller 变换:将均匀分布转换为标准正态分布 (高斯分布)
float2 BoxMuller(float u1, float u2)
{
u1 = max(u1, 1e-6); // 防止 log(0)
float R = sqrt(-2.0 * log(u1));
float Theta = 2.0 * PI * u2;
return float2(R * cos(Theta), R * sin(Theta));
}
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
// 略
// 计算频谱
float S_k = JONSWAP(k);
// 计算振幅
float dk = 2.0 * PI / Length;
float amp = sqrt(S_k * dk * dk / 2.0);
// 生成随机数 (使用坐标组合作为种子确保确定性)
uint seed = WangHash(id.x + id.y * Size);
uint seed2 = WangHash(seed);
float2 xi = BoxMuller(RandomFloat(seed), RandomFloat(seed2));
// h0
float2 h0_k = xi * amp;
}