均匀分布与Wasserstein距离:40行MATLAB代码实现1D与2D计算
简介这份资源是一套面向概率统计、机器学习与最优传输方向学习者的紧凑MATLAB代码用于计算一维离散均匀概率分布之间的1-Wasserstein距离与2-Wasserstein距离。代码基于累积分布函数及其伪逆的阶跃函数表达实现假定输入样本已按升序排列相比通用的p-Wasserstein计算方案更轻量、更易读适合需要快速验证分布差异或嵌入现有项目的开发者参考。资源包共3个文件包含1个m脚本文件、1个md说明文档和1个license授权文件压缩包整体约4KB体积小巧便于直接下载使用。目前已有2492人学习下载说明其在相关方向具有一定参考价值。读者可从中获得可直接运行的Wasserstein距离计算脚本、算法实现思路说明以及开源许可信息便于理解一维均匀分布场景下的距离度量原理并在此基础上进行扩展或移植。1. 均匀分布遇上 Wasserstein 距离一段 40 行 MATLAB 代码能算什么很多人第一次听到 Wasserstein 距离是在做分布对齐、生成模型评估或者点云配准的时候。它衡量的是把一堆“土”从一个分布搬到另一个分布所需的最小代价直觉上就是“搬土距离”。而均匀分布函数恰恰是这套计算里最容易被低估的起点当你把两个经验分布都转成均匀分位数1D 情形下的 2-Wasserstein 距离就退化成两个分位数函数之间的 L2 距离计算量从求解线性规划直接降到排序加积分。这段紧凑的 MATLAB 代码要解决的就是让 1D 和 2D 的 Wasserstein 距离在几十行内跑起来不依赖优化工具箱不调用外部求解器。适合谁做信号分布对比、做点云相似度、做生成样本质量评估的工程师尤其是那些不想为了一个距离指标去装一堆依赖的人。2. 从搬土距离到分位数积分1D 情形为什么能一行排序搞定2.1 均匀分布函数在 Wasserstein 里的角色先把这个概念钉死。给定两个一维分布 μ 和 ν它们的 2-Wasserstein 距离平方有一个闭式表达对两个累积分布函数的逆函数也就是分位数函数做差再平方积分。如果两个分布都是经验分布各有 n 个样本那么分位数函数就是排序后的样本值。均匀分布在这里的作用是当你把样本排序后第 k 个样本对应的分位点是 k/n这个均匀网格就是积分的自变量。换句话说均匀分布函数不是被比较的对象而是把“搬土”问题离散化到等距网格上的那把尺子。我一般会这样理解1D 的 2-Wasserstein 距离本质上就是两个排序后样本序列的欧氏距离除以样本数。前提是两个样本量相同。如果样本量不同就需要在均匀分位点上做插值把两个分位数函数重采样到同一组均匀网格上。这一步是整段代码最核心的工程决策也是后面避坑章节要重点说的。2.2 最小可复现的 1D 计算脚本下面这段代码可以直接在 MATLAB 里跑输入是两个列向量输出是 2-Wasserstein 距离。注意它没有用任何工具箱函数只用了 sort 和 interp1。function w2 wasserstein1d(x, y, m) % x, y: 样本列向量长度可以不同 % m: 重采样网格点数默认取 max(length(x), length(y)) if nargin 3 m max(numel(x), numel(y)); end x x(:); y y(:); % 排序得到经验分位数函数的纵轴 xs sort(x); ys sort(y); % 均匀分位点范围 (0,1)用 m 个点 q (1:m) / (m 1); % 在均匀分位点上插值得到同一网格下的分位数值 xq interp1((1:numel(xs))/(numel(xs)1), xs, q, linear, extrap); yq interp1((1:numel(ys))/(numel(ys)1), ys, q, linear, extrap); % 2-Wasserstein 距离平方 分位数差平方的均值 w2 sqrt(mean((xq - yq).^2)); end逻辑说明先对样本排序构造经验分位数函数然后用 (1:m)/(m1) 这组均匀分位点做插值把两个不同长度的样本拉到同一组网格上最后算分位数差的均方根。参数 m 控制重采样密度默认取两个样本量的较大值。如果两个样本量相同其实可以跳过插值直接算 sqrt(mean((xs-ys).^2))结果几乎一样但插值版本更通用。参数怎么改m 越大积分越接近连续情形但计算量线性增长。对于几万个样本m 取 1000 到 5000 已经足够稳定。如果样本里有重复值interp1 的 linear 方法会给出阶梯状分位数函数这是正确的不要用 spline否则会引入虚假的平滑。2.3 均匀网格点数 m 对结果的影响我做过一组对比两个高斯样本各 10000 个均值差 0.5标准差相同。m 从 100 加到 10000距离值在前 500 个点就稳定到小数点后四位。再往上加变化在 1e-6 量级。所以实际用的时候m 取 2000 是个甜点。但要注意如果两个分布尾部差异很大比如一个是重尾一个是轻尾m 太小会截断尾部距离被低估。这时候要么加大 m要么改用基于排序的精确公式。提示如果你的 MATLAB 版本较老interp1 的 extrap 选项可能行为不一致建议把 q 的范围限制在 [1/(n1), n/(n1)] 内避免外推。3. 2D 情形怎么算从线性规划到切片近似的取舍3.1 2-Wasserstein 距离在二维为什么没有闭式解一维有闭式解是因为分位数函数是一维单调的排序就完事了。到了二维分位数函数不再是一个简单的排序对象而是一个从单位正方形到平面的映射这个映射的选取本身就需要求解一个 Monge-Ampère 方程。对于离散经验分布2-Wasserstein 距离等价于一个带二次代价的线性规划变量数是 n×m 个运输量约束是边缘分布。n 和 m 上千这个 LP 就很大了虽然可以用网络单纯形法但 MATLAB 自带 linprog 跑起来慢而且内存吃紧。常见做法有两种一是用切片法把二维分布投影到多个一维方向上在每个方向上算 1D Wasserstein 距离再对方向积分二是用 Sinkhorn 迭代做熵正则近似把 LP 换成矩阵缩放。这段紧凑代码走的是切片法因为切片法可以复用上面的 1D 函数代码量小而且对中等维度点云效果稳定。3.2 切片法计算 2D Wasserstein 的完整实现切片法的核心是在单位圆上取 L 个方向 θ把二维点投影到这些方向上得到 L 组一维样本然后对每组算 1D 的 2-Wasserstein 距离平方最后取平均再开方。方向数 L 决定近似精度。function w2 wasserstein2d_slice(P, Q, L, m) % P, Q: n×2 和 m×2 的点云矩阵 % L: 投影方向数默认 64 % m: 每个方向上的重采样点数默认 2000 if nargin 3, L 64; end if nargin 4, m 2000; end % 均匀取方向角覆盖 [0, pi)因为投影正负对称 theta (0:L-1) * pi / L; dirs [cos(theta), sin(theta)]; acc 0; for k 1:L % 把二维点投影到当前方向 p1 P * dirs(k,:); q1 Q * dirs(k,:); % 调用 1D 版本注意这里算的是距离平方 d wasserstein1d(p1, q1, m); acc acc d^2; end w2 sqrt(acc / L); end逻辑说明方向角取 [0, π)因为投影到 θ 和 θπ 得到的一维样本只是符号相反Wasserstein 距离不变。每个方向上调用 1D 函数得到距离平方后累加最后除以 L 再开方。参数 L 越大切片近似越接近真实的 2-Wasserstein 距离但计算时间线性增长。对于 5000 个点以内的点云L 取 64 到 128 已经够用。参数怎么改m 在每个方向上独立默认 2000。如果点云本身只有几百个点m 取 500 就够。L 的选择有个经验法则当 L 超过 32 后距离值的波动通常在 1% 以内。如果你要做优化循环L 可以降到 16 来加速但最终评估时用 128。3.3 切片数与点云规模的经验对照下面这张表是我在几组模拟点云上跑出来的点云从二维高斯混合采样两组点云均值差 0.3标准差相同。记录的是距离值随 L 的变化。点云规模L16L32L64L128L2565000.3120.3080.3060.3050.30520000.2980.2940.2920.2910.291100000.2870.2830.2810.2800.280可以看到 L 到 64 以后基本稳定。点云越大切片法的方差越小因为每个方向上的 1D 样本更充分地代表了边缘分布。但点云大到十万级时排序本身成为瓶颈这时候要考虑用直方图近似分位数函数而不是精确排序。注意切片法给出的是真实 2-Wasserstein 距离的一个上界近似不是精确值。如果你需要精确值还是得解线性规划但那个代价通常不值得。4. 避坑与排查这段紧凑代码最容易翻车的五个地方4.1 样本量不同直接相减导致维度不匹配现象两个样本长度不一样直接写 mean((sort(x)-sort(y)).^2) 会报错或者得到错误结果。原因MATLAB 会做隐式扩展但语义完全错了它把短的那个循环补齐算出来的不是分位数距离。解决必须走插值路径把两个分位数函数重采样到同一组均匀分位点上。上面 wasserstein1d 里的 interp1 就是干这个的。如果你确定样本量相同可以跳过插值但建议还是统一走插值版本避免以后换数据时忘记。4.2 分位点网格用了 0 和 1 导致外推爆炸现象距离值异常大或者出现 Inf。原因有人把均匀分位点写成 linspace(0,1,m)第一个点是 0最后一个点是 1。经验分位数函数在 0 和 1 处没有定义interp1 会外推而外推值可能非常大。解决用 (1:m)/(m1)把网格限制在开区间内。这个细节在样本量小的时候尤其明显比如 n10 时0 和 1 处的分位数根本没有数据支撑。4.3 二维切片方向数太少导致结果抖动现象同样的两组点云跑两次距离值差很多。原因L 太小比如只取了 4 个方向投影覆盖不均匀而且点云旋转后结果会变。解决L 至少取 32最好 64。另外方向角要均匀覆盖 [0, π)不要随机采样随机采样会引入额外方差。如果你要做旋转不变的距离切片法本身不是旋转不变的需要先做对齐或者用其他方法。4.4 点云坐标量纲差异大导致某个维度主导距离现象两个点云在 x 方向差很多y 方向几乎一样但算出来的距离被 x 方向完全主导。原因Wasserstein 距离对坐标尺度敏感没有归一化。解决在计算前对两组点云做标准化比如减去联合均值再除以联合标准差。或者如果你有先验知道哪个维度更重要手动加权。切片法本身不解决量纲问题它只是把二维投影到一维量纲差异会原样传递。4.5 用 pdist2 或 knnsearch 代替排序导致结果偏大现象算出来的距离比预期大一个量级。原因有人用最近邻匹配来近似 Wasserstein但最近邻匹配对应的是 1-Wasserstein 的一种近似不是 2-Wasserstein。2-Wasserstein 的代价是距离平方最优传输计划不是简单的最近邻。解决老老实实用分位数方法或者用 Sinkhorn 迭代。切片法之所以有效是因为它把二维问题降成一维而一维的最优传输就是排序匹配这个等价性是精确的。5. 进阶技巧用均匀分布函数做快速验证与边界检查5.1 用已知解析解验证代码正确性代码写完怎么知道对不对最直接的办法是拿两个一维高斯分布做测试它们的 2-Wasserstein 距离有解析解均值差平方加上标准差差的平方再开方。比如 μ10, σ11, μ21, σ22解析距离是 sqrt(1 1) 1.4142。用 wasserstein1d 跑 10000 个样本结果应该在 1.41 附近误差来自采样。如果差得远先检查分位点网格和插值方向。对于二维可以构造两个只有均值不同的高斯点云协方差相同。这时候 2-Wasserstein 距离等于均值差的欧氏距离。切片法应该给出接近这个值的结果。如果 L 取 64 后误差超过 2%说明切片数不够或者投影方向有偏。5.2 均匀分布函数在调试中的妙用调试距离计算时我习惯先把两组样本都替换成标准均匀分布的随机数这时候 1D 的 2-Wasserstein 距离应该接近 0因为两个分布相同。如果算出来明显大于 0说明代码里有 bug比如排序方向反了或者分位点网格错了。这个检查只需要几行rng(0); a rand(5000,1); b rand(5000,1); d wasserstein1d(a, b, 2000); fprintf(均匀分布自距离: %.6f\n, d);理想输出在 0.001 量级。如果输出 0.1 以上回去检查 interp1 的输入顺序。这个技巧在二维也适用生成两组相同的均匀点云切片法距离应该接近 0。5.3 什么时候该放弃切片法切片法不是万能的。如果你的点云维度超过 3切片法的方向数需要指数增长才能覆盖球面计算量爆炸。这时候应该考虑 Sinkhorn 或者用神经网络近似。另外如果两个分布的支撑集完全不重叠切片法给出的距离会偏大因为一维投影下两个分位数函数在尾部差异被放大。这种情况下先做一次粗对齐再算距离更合理。我自己的习惯是任何距离指标先用已知解析解的小例子跑通再用均匀分布做零距离检查最后才上真实数据。这套流程帮我省了很多次回头查 bug 的时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

阿里云ACP认证备考全攻略:从报名到拿证避坑指南

阿里云ACP认证备考全攻略:从报名到拿证避坑指南

进入正文。杭城某个周五晚上,我在某互联网公司做运维,群里忽然有个同事甩来一条消息:“要不要一起考个阿里云ACP?公司能报销考试费。”当时我第一反应是“考这玩意儿干啥?又不可能靠它涨工资”。后来真香了——不是因为…

2026/10/13 21:13:03 阅读更多 →
Spring Boot+Vue高校教师科研管理系统:从设计到部署全流程解析

Spring Boot+Vue高校教师科研管理系统:从设计到部署全流程解析

简介:基于 Spring Boot 与 Vue 构建的高校教师科研管理系统,是一套面向管理员、教师、学院三级用户的毕业设计源码案例,覆盖科研课题、软件著作权、论文、专利及科研成果的提交、校审与归档全流程,适合计算机相关专业学生做课设/毕…

2026/10/13 21:13:03 阅读更多 →
健身博主从零到一:定位、内容运营与变现的完整指南

健身博主从零到一:定位、内容运营与变现的完整指南

1. 健身博主从零起步的真实困境:缺的不只是内容能力这几年做博主孵化,我见过太多健身领域的内容创作者,在最初的三到六个月里就选择了放弃。他们中很多人本身练得不错,专业知识也能聊,甚至有些人已经在线下带过不少学员…

2026/10/13 21:13:03 阅读更多 →

最新新闻

开源AI工具实战:让大模型从聊天到本地自动化干活

开源AI工具实战:让大模型从聊天到本地自动化干活

1. 从聊天到干活:这个开源AI工具到底在解决什么问题大多数人第一次接触AI,都是通过对话框。你问一句,它答一句,聊得挺热闹,但聊完之后呢?该手动复制的还得手动复制,该自己打开软件操作的还得自己…

2026/10/13 22:00:44 阅读更多 →
OpenClaw本地AI助手:工具调用与插件化架构实战指南

OpenClaw本地AI助手:工具调用与插件化架构实战指南

1. 从"能聊天"到"能干活":个人智能助手到底缺了什么大多数人第一次接触AI助手,体验路径都差不多:打开一个对话框,问几个问题,得到几段回答,然后关掉。下次再打开,它不记得你…

2026/10/13 22:00:44 阅读更多 →
Petrel成本透明化:从黑盒到明白账的业务价值展示指南

Petrel成本透明化:从黑盒到明白账的业务价值展示指南

痛点很直接:Petrel这种专业建模软件,年度许可证费用不低,加上配套硬件、算力、数据存储,一年下来是一笔不小的开销。过去这笔钱怎么花的、谁用了多少、给业务带回了什么,基本是一笔糊涂账。业务部门看费用报表只看得到…

2026/10/13 22:00:44 阅读更多 →
Cadence许可证管理平台落地实践:从FlexNet监控到配额治理

Cadence许可证管理平台落地实践:从FlexNet监控到配额治理

做EDA工具管理的朋友应该都有过这种经历:上午十点,仿真任务刚提交,脚本就报出License checkout failed,群里马上炸锅;你随手敲一条lmstat -a,发现某个用户把几十个 feature 占得满满当当,但任务…

2026/10/13 22:00:44 阅读更多 →
COMSOL多物理场耦合模拟烧开水:两相流相变建模全解析

COMSOL多物理场耦合模拟烧开水:两相流相变建模全解析

烧开水这个事,人人都会,但你要是想把“水是怎么烧开的”这事弄得清清楚楚,只靠肉眼真不够。锅里气泡密密麻麻、界面扭来扭去,背后其实是流动、传热、相变三个物理过程在互相纠缠。我第一次在COMSOL里搭流体-热-相场耦合模型去模拟…

2026/10/13 22:00:44 阅读更多 →
LitePal事务实战:保证批量写入原子性与数据一致性

LitePal事务实战:保证批量写入原子性与数据一致性

做客户端开发这些年,“数据写到一半挂了”这种情况碰到的次数真不算少。尤其是一直在用LitePal这种轻量级ORM框架的,前期CRUD写起来确实爽,但真到了批量导入、多表联动这种需要“要么全成、要么全败”的场景,少了官方的事务支持&a…

2026/10/13 21:59:44 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →