2. 数学基础:欧几里得距离与均方根公式

做 RMSD 计算,说白了就是算两个结构之间的“像不像”。背后的数学工具,一个是 欧几里得距离,一个是 均方根。这两个概念你要是理解透了,后面写代码调试结果就顺了。

我个人习惯,先弄清楚单个原子怎么算“差多远”,再推广到整个分子。虽然高中数学就讲过距离公式,但放到高维坐标里,很多人就容易懵。今天咱们从头捋一遍。

2.1 欧几里得距离:空间里的“直线距离”

欧几里得距离,就是你初中就学过的两点间距离公式。在二维平面里:

d = sqrt( (x2 - x1)^2 + (y2 - y1)^2 )

三维空间里,加个 z 坐标:

d = sqrt( (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2 )

这有什么可讲的?但是,你在做分子模拟时,每个原子有 三维坐标,整个分子有 N 个原子。当我们说“两个构象之间的距离”,往往是指把每个原子的坐标差平方求和开根号?不对,那是整体RMSD的基础。这里先搞清楚:单个原子的位移,就是三维欧几里得距离。

小提示: 欧几里得距离只适用于 笛卡尔坐标系。如果你处理的是内坐标(键长、键角、二面角),距离定义就不一样了。我在早期的一个项目里,直接把内坐标当笛卡尔坐标算距离,结果全错了——尴尬。

当一个分子有 N 个原子时,我们实际上有一个 3N 维的超空间。一个构象就是一个 3N 维点。两个构象之间的欧几里得距离就是:

D = sqrt( sum_{i=1 to N} [ (xi' - xi)^2 + (yi' - yi)^2 + (zi' - zi)^2 ] )

嗯,注意这里是先对所有坐标差平方求和,再开根。这其实已经和 RMSD 很像了,就差一个“平均”步骤。

2.2 均方根(RMS):先平方,再平均,最后开方

均方根(Root Mean Square)是一种统计量,它把一组数值先平方,求平均,再取平方根。公式长这样:

RMS = sqrt( (1/n) * sum_{i=1 to n} x_i^2 )

你看,这和标准差很像,但没有减去平均值。RMS 强调 数值的大小,经常用来衡量信号强度、误差幅度。比如电压的有效值就是 RMS。在结构比对里,我们关心的是“每个原子位移的典型大小”,而不是它们的平均值(平均值可能正负抵消变成0)。

关键区别: 如果你对所有位移直接求平均,正位移和负位移会互相抵消——哪怕每个原子都动了很大,平均位移可能还是0。RMS 通过平方避免了这个问题。

2.3 从距离到 RMSD:坐标差 → 平方 → 平均 → 开方

RMSD 的全称是 Root Mean Square Deviation(均方根偏差)。它计算的是两个分子构象中 对应原子 之间位移的 RMS 值。数学定义:

RMSD = sqrt( (1/N) * sum_{i=1 to N} || r_i - r_i' ||^2 )

其中 || r_i - r_i' || 就是第 i 个原子的欧几里得距离(三维)。N 是原子总数。

看到没?RMSD 其实是 平均后的欧几里得距离。如果你把所有原子的位移平方求和,除以 N,再开方,就得到了 RMSD。这个值可以直接理解成“每个原子平均偏移了多少”,单位是 Å(埃)。

欧几里得距离 vs RMSD
概念 对象 公式(简化) 物理含义
欧几里得距离 单个原子(3维) sqrt(Δx²+Δy²+Δz²) 该原子位移的长度
总欧几里得距离 整个分子(3N维) sqrt(∑Δx²+…) 所有原子位移总和的“长度”
RMSD 整个分子 sqrt( (1/N) * ∑(Δx²+…) ) 平均每个原子的位移大小

注意: 有些论文里 RMSD 的公式没有开号——那是定义混乱了。RMSD 一定要有平方根,不然叫 MSD(均值平方偏差)。我曾经审稿时看到一篇论文把 MSD 当 RMSD 用,数据差了一个量级。

2.4 手算一个例子:加深理解

咱们拿一个假想分子,只有 3 个原子。参考构象坐标:

原子1: (0.0, 0.0, 0.0)
原子2: (1.0, 0.0, 0.0)
原子3: (0.0, 1.0, 0.0)

目标构象坐标(经过了一些平移和扭曲):

原子1: (0.1, 0.2, 0.0)
原子2: (1.1, -0.1, 0.1)
原子3: (-0.1, 1.2, -0.1)

第一步:算每个原子的位移(欧几里得距离):

原子1: sqrt((0.1-0.0)^2 + (0.2-0.0)^2 + (0.0-0.0)^2) = sqrt(0.01+0.04+0) = sqrt(0.05) ≈ 0.224
原子2: sqrt((1.1-1.0)^2 + (-0.1-0.0)^2 + (0.1-0.0)^2) = sqrt(0.01+0.01+0.01) = sqrt(0.03) ≈ 0.173
原子3: sqrt((-0.1-0.0)^2 + (1.2-1.0)^2 + (-0.1-0.0)^2) = sqrt(0.01+0.04+0.01) = sqrt(0.06) ≈ 0.245

第二步:对位移的平方求和,然后除以原子数,再开根:

平方和 = 0.05 + 0.03 + 0.06 = 0.14
RMSD = sqrt(0.14 / 3) = sqrt(0.04667) ≈ 0.216 Å

你看,RMSD 差不多是三个原子位移的平均水平(0.224、0.173、0.245 的平均值约 0.214,很接近)。

实用经验: 很多代码库直接算 RMSD 时并不显式计算每个原子的欧几里得距离,而是把所有坐标差平方一次性累加,最后除以 N 再开方。一个常见的 numpy 片段:

diff = coords_ref - coords_target
rmsd = np.sqrt(np.mean(np.sum(diff**2, axis=1)))

这里 diff**2 是每个轴差平方,np.sum(axis=1) 得到每个原子的位移平方,np.mean(...) 求平均,最后 np.sqrt。简洁直观。

2.5 常见误区与避坑指南

  • 忘记对齐:RMSD 定义要求 先对齐 两个构象(去除刚体平移和旋转)。如果直接拿原始坐标算,结果会包含整体位移和旋转的贡献,不是真正的构象差异。我刚开始学的时候,直接用 PDB 的原始坐标算 RMSD,数值奇大无比,后来才知道要先做 Kabsch 对齐。
  • 权重问题:有些 RMSD 计算会按原子质量加权(比如只算骨架原子或所有重原子)。公式变成了 RMSD = sqrt( (1/∑w_i) * ∑ w_i * d_i^2 )。加权均方根在蛋白质比较中很常用。
  • 维度陷阱:上面例子只用了 3 个原子,实际蛋白质动辄上千原子。RMSD 计算量不大,但内存要注意:如果你用 Python 循环来算,会很慢;用 numpy 向量化几乎是瞬间的。

个人建议: 写 RMSD 计算函数时,一定要明确输入坐标的形状。我最常用的约定是 shape = (N, 3) 的数组,对应 N 个原子的 x,y,z。如果你用 (3,N) ,记得转置。否则结果会变成计算 x 坐标之间的 RMSD——那就不对了。

从坐标到 RMSD 的计算流程 参考构象坐标 目标构象坐标 先做刚体对齐 每个原子的位移向量 (Δx, Δy, Δz) 各轴平方 → 求和 → 再求和 除以原子数 N 后开根 最终结果:RMSD (Å) 单位与坐标一致

这张图把我刚才讲的流程串起来了:两个构象先对齐,然后逐原子算位移,再平方求和,最后平均再开根。你写代码的时候就在心里过一遍这个图,不容易漏步骤。

好了,数学基础就这些。别看公式简单,实际项目中我遇到过好多次算出来的 RMSD 比预期大很多,一排查发现是“算之前没对齐”或者“把氢原子也算进去了”。所以,记住:对齐、选原子、平方、平均、开方,五个步骤缺一不可。