尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MATLAB伪随机数生成全解析:从rand到rng的实践指南
很多人第一次在MATLAB里敲下rand(1,10)看到一屏幕0到1之间的小数心里想的是这不就是随机数嘛。后来如果做蒙特卡洛模拟或者要把这批数拿去做加密密钥就会有前辈提醒你这些数其实是伪随机数不是真随机数。这中间的差别不是一句“差不多”就能带过去的。这篇文章想把这几个概念一次说清楚什么是真随机数、什么是伪随机数以及MATLAB里伪随机数到底是怎么生成的、怎么用好它。内容适合刚接触随机模拟的学生也适合想把自己的实验做得更严谨的研究者。我会从底层原理讲到具体函数再给出一套能直接抄的代码模板以及几个我自己踩过的坑。1. 真随机数与伪随机数看似相似逻辑完全不同1.1 真随机数靠物理世界不可预测也永不重复真随机数True Random Number GeneratorTRNG的源头不在软件里而在物理过程中。放射性衰变、半导体热噪声、光子通过半透镜的路径、大气噪声的采样这些过程在本质上都有不可预测性。你想提前算出一个真随机序列的下一项唯一的方法大概就是复刻整个物理条件这在现实里基本不可能。即便拿到了此前所有输出下一项仍然与它们没有确定的函数关系。但“不可预测”不等于“均匀分布”。很多物理随机源直接采出来的数据比如热噪声的电压值往往带有偏差或者自相关。实际设备还需要做后处理例如哈希提取、累加去偏把原始物理噪声“洗”成一份干净的随机比特。也就是说真随机数强调的是来源不保证输出完美。工程上通常把物理熵源和提取算法打包成一个黑盒子对外提供随机字节。1.2 伪随机数靠确定性算法种子一决定终身伪随机数是另一个思路用一个确定性数学公式从一个初始状态种子 Seed出发不断迭代产生一串在统计性质上很像随机序列的数。因为公式固定所以给定同样的种子和生成器序列永远一样。最简单的线性同余发生器Linear Congruential GeneratorLCG就是x_{n1} (a * x_n c) mod m只要参数选得好输出看起来足够乱但它本质上是一串确定数列。这里有个关键概念伪随机数不是“假到不能用”而是“假到有迹可循”。如果应用只看统计分布特征比如平均值、方差、直方图形状伪随机数完全够用如果应用要求攻击者无法从输出反推状态那普通伪随机就不合格必须换用安全级别更高的密码学安全伪随机数生成器CSPRNG。1.3 为什么绝大多数模拟场景用伪随机数就够了我做数学实验的时候绝大多数场景要的不是不可预测而是“可复现的随机性”。什么意思比如你想比较算法A和算法B在随机数据上的表现如果每次实验数据都随机变化你没法判断性能差异是算法造成的还是数据碰运气造成的。这时伪随机数最大的优点就体现出来给定相同的种子数据序列完全一致算法对比变成严格的控制变量实验。其次伪随机数生成速度极快。MATLAB里rand(1e6,1)也就是眨眼的功夫真随机数如果从硬件读取可能受限于采样速率和接口吞吐慢几个数量级。再加上真随机数无法两次拿到同一序列调试出错时无法重现场景。所以除非涉及安全性、公平性这类对抗场景否则我几乎不会为了“更随机”而去追求真随机。1.4 真随机和伪随机选错了会有什么后果选错类型后果不一定马上显现但真要出事就是大事。第一类错误在加密应用里用了普通伪随机序列。攻击者如果拿到一部分输出或者猜出种子就能重建整个密钥。第二类错误在蒙特卡洛模拟里用了周期太短的伪随机数。早期一些生成器周期只有2^31甚至更短模拟跑得长一点数据就会重复出现结果偏得离谱。第三类错误在抽奖、竞猜类系统里用了可预测的随机数。只要别人摸清种子规律就能算出后续结果。这已经不只是技术问题而是公平性问题。所以选择随机数类型先问自己一个问题我的数据会不会被一个足够聪明的对手观察、预测、利用会走真随机或CSPRNG不会那伪随机在绝大多数情况下反而是更理性、更高效的选择。2. MATLAB默认的伪随机生成器机制从LCG到梅森旋转2.1 LCG一句话讲清楚伪随机生成器的老祖宗为了理解MATLAB为什么默认用梅森旋转先理解LCG。LCG的递推式是x_{n1} (a * x_n c) mod m。x是一个整数每次取模后落在[0, m-1]。把x/m当成[0,1)区间的小数就得到一条伪随机数序列。它的周期最多等于m但只有参数选得好才能达到这个上限参数选得不好可能只生成一小段数就进入循环。LCG实现极简单早期很多编程语言的rand都是它。可它有一个让人头疼的毛病如果m不够大序列周期短、结构性强。把连续几个数映射到高维空间会发现它们容易落在少数超平面上这就是所谓的“格结构”。对严格的蒙特卡洛问题这种隐藏相关会直接影响结果。这也是为什么现代环境很少再用裸LCG作为默认随机数来源。2.2 Mersenne TwisterMATLAB默认生成器mt19937arMATLAB目前默认的rand生成器是梅森旋转算法Mersenne Twister具体是 mt19937ar。它的核心思想是维护一个624维的状态向量通过线性反馈移位寄存器不断产生新状态再对状态做位运算和变换输出32位整数或对应的均匀小数。这个算法的周期达到2^19937 - 1这是一个天文数字。全宇宙原子数量估计在10^80量级而2^19937大概接近10^6000你在模拟里根本用不完。它的均匀性、高维分布特性都远好于LCG生成速度也很快。但要注意梅森旋转不是密码学安全生成器。换句话说看到它输出的一段数值理论上可以反推出内部状态。所以MATLAB内置随机数可以用在模拟、抽样、初始化不要用于密码学密钥生成。如果你确实需要用MATLAB做安全随机应该走Java SecureRandom或外部专门库。MATLAB里常用的生成器还有下面这些生成器类型特点适用场景twister默认的梅森旋转速度快周期巨大通用模拟、抽样mrg32k3a支持多个独立流和子流并行计算、多线程任务philox/threefry现代计数器型生成器并行友好需要大量独立随机流的场景multFibonacci多重滞后斐波那契周期长部分老代码兼容场景combRecursive组合递推生成器周期非常长大规模科学计算2.3 种子Seed到底怎么影响随机数序列种子是伪随机生成器的起点。同一个生成器算法加上同一个种子产生的序列完全相同种子变了序列会完全换一套。MATLAB里rng(1)设置种子为1rng(shuffle)则根据当前时间等信息生成一个随机种子。这里有个容易混淆的点rng(shuffle)不是真随机它只是让种子变得难以预知之后的序列仍然是确定性算法产生的。另一个重要细节是MATLAB每次启动时默认会把生成器状态重置为种子0。这会导致什么现象你新开一个MATLAB不写任何rng设置直接运行rand(1,3)得到的结果和上一次新开MATLAB时一模一样。这是为了可复现性特意设计的。如果你希望每次运行都不同请在开头显式调用rng(shuffle)。2.4 从均匀分布到任意分布随机数生成的本质思路所有分布生成的底层起点都是[0,1)均匀分布伪随机数。得到均匀数之后怎么变成正态、指数、泊松常见有三条路。第一是逆变换法求出目标分布的累积分布函数F然后令x F^{-1}(U)这里U是均匀数。第二是变换法例如 Box-Muller 方法用两个均匀数生成一个标准正态数。第三是拒绝采样适合密度函数形式复杂的分布。MATLAB把这条路封装得很干净。randn生成标准正态random(Gamma,...)生成任意常见分布底层算的都是伪随机均匀数。不建议每次都手动写变换但理解这一点很重要以后如果遇到MATLAB没有现成函数的分布你知道可以直接用逆变换或拒绝采样去造而不是干瞪眼。3. MATLAB中生成伪随机数的完整实操从rand到rng3.1 最常用的四个函数rand、randi、randn、randperm先给一段可以直接跑的代码% 生成 3x4 的 [0,1) 均匀分布矩阵 U rand(3,4); % 生成 1x10 的整数取值 1 到 100 I randi([1,100], 1, 10); % 生成 1000x1 的标准正态分布 Z randn(1000,1); % 将 1:10 随机打乱 P randperm(10);几个容易踩的点。rand(3)生成的不是1x3而是3x3方阵想生成向量一定要写清楚行数和列数。randi的上界必须包含要的数值比如要1到100写成randi(100)也行它等价于randi([1,100])。randn输出的是标准正态均值为0、方差为1要得到均值mu、标准差sigma的正态数写成mu sigma*randn(m,n)。randperm最常用于实验分组、抽样排序。比如要把100个样本随机分成训练集和测试集大多数人是先写randperm(100)再取前80个下标作为训练集剩下20个作为测试集。这样写比rand(100,1)再排序稳定得多。3.2 rng与RandStream控制随机流的推荐姿势rng是控制全局随机数生成器的统一入口。固定种子的标准写法rng(2025); A rand(1,5); rng(2025); B rand(1,5); % A 和 B 完全一样这段代码说明可复现性是怎么来的。如果你只想要特定的一段数据可复现不用重启可以捕获状态s rng; % 记下当前状态 data1 rand(1000,1); rng(s); % 恢复刚才状态 data2 rand(1000,1); % 与 data1 完全相同全局随机流适合简单脚本。但如果你同时跑多个模型我推荐用RandStream创建独立流每个模型各用各的避免互相污染streamA RandStream(mt19937ar,Seed,123); streamB RandStream(mt19937ar,Seed,456); xA rand(streamA,100,1); xB rand(streamB,100,1);RandStream的另一个好处是可以给不同 worker 分配不同流这在并行计算里尤其重要后面第4节再展开。3.3 指定分布与手动变换random、makedist 和逆变换如果你要正态、指数、泊松等常见分布最简单的是用random函数rng(42); x random(Normal, 2, 0.5, [1000,1]); pd makedist(Exponential, 1.5); y random(pd, 1000,1);random的第一个参数是分布名后面跟参数最后是输出尺寸。makedist适合一次性建立分布对象之后多次取样、求解分位数都很方便。不过要真正理解随机数手动变换一次比调用函数收获大。比如指数分布累积分布函数是F(x)1-exp(-lambda*x)反函数是x-log(1-u)/lambda于是lambda 1.5; u rand(1000,1); x_exp -log(1 - u) / lambda;注意这里用1-u而不是u是为了避免u0时出现log(0)。这个细节教科书常写但真写代码时最容易漏。逆变换法的效率不一定最高但思路最直观当你需要自定义分布时它是第一块跳板。3.4 一个能直接上手的例子用蒙特卡洛模拟估算π空讲一堆概念不如来一个经典案例。圆面积公式A pi * r^2取r 1那么单位圆在第一象限的面积是pi/4。往1x1方形里撒点统计落在半径为1的扇形内的比例乘以4就是pi的估计。MATLAB实现function pi_est estimate_pi(n) rng(12345); % 固定种子保证可复现 x rand(n,1); y rand(n,1); inside x.^2 y.^2 1; pi_est 4 * sum(inside) / n; end调用pi_est estimate_pi(10000); err abs(pi_est - pi);这个例子有三个要点。第一rand(n,1)生成的是n个点坐标独立均匀点落在方形内任意位置概率相等。第二inside是逻辑向量sum(inside)统计成功个数MATLAB会自动把true当1。第三估计误差大致按1/sqrt(n)下降n1e4时误差大约在百分之几n1e6时误差能到千分之几。多次运行固定种子下结果不变这就是伪随机数带来的实验可复现性。3.5 论文级可复现实验的三个建议我见过很多代码算法很漂亮但随机部分写得一塌糊涂脚本跑到一半rng(shuffle)结果别人复现时数值全对不上。想在论文或正式项目里做到可复现至少做到三件事第一脚本开头固定种子。rng(42)写在所有随机调用之前。第二把生成器类型、种子、运行环境版本一起记录下来。比如在文件头部写% rand: mt19937ar, seed42, MATLAB R2023a。第三如果你的核心结果依赖随机采样不要把希望压在一次种子上。可以写一个外层循环遍历多个种子跑多组结果最后报告均值和方差再把随机种子列表存成变量放进结果文件。这样别人既能复现单次也能看到结论的稳健性。4. 随机数质量问题、复现陷阱与排查实录4.1 为什么每次运行结果一样或者不一样这套问题排在所有随机数问题榜首。原因很简单MATLAB默认的全局随机流在每次启动时种子固定为0。所以如果你不调用rng直接执行rand(1,3)每次启动MATLAB得到同一个三元组。注意这里说的是启动不是说脚本里每次都一样。同一个MATLAB进程内多次运行同一个脚本默认流会一直往前推进所以结果一般不同。如果你希望每次运行都不同就在脚本开头写rng(shuffle)。如果你希望每次运行都一样就写rng(固定数字)。两者不能贪心我知道有人想“第一次不同后面固定”就会在循环里反复重设种子结果把原本独立的数据搞成高度相关这比随机的偏差还可怕。4.2 怎么快速检查一批随机数质量正规的随机性检验有很多实际使用中我不会每次都跑全套而是先做三个快速检查。第一看分布直方图均匀数应该大致平直第二看均值和方差[0,1]均匀数的理论均值0.5理论方差1/12 ≈ 0.0833第三做卡方检验把[0,1]分成k个等宽区间统计观测频数obs和期望频数expc n/k计算统计量sum((obs-expc).^2 ./ expc)再和卡方分布比较。MATLAB代码u rand(10000,1); edges linspace(0,1,21); obs histcounts(u, edges); expc numel(u) / numel(obs); chi2stat sum((obs - expc).^2 / expc); df numel(obs) - 1; p chi2cdf(chi2stat, df, upper);p值小于0.05时要注意但不代表一定有问题。随机数本来就会以5%的概率“不合格”。我通常会换种子再测几次如果经常出现异常才怀疑生成器或代码有 bug。4.3 旧接口 rand(seed) 到底哪里不好如果你在网上找老代码会看到rand(seed, 1)这种写法。MATLAB确实还兼容但不再推荐使用。旧接口当年控制的是旧算法的全局状态行为与现代rng不兼容。新版调用它会产生警告甚至可能引入全局状态混乱。更实际的问题是rand(seed, 1)只能用旧的生成器无法利用默认的梅森旋转生成质量、周期、可管理性都不如rng。把老代码迁移过来时我建议直接把所有rand(seed,...)和randn(seed,...)替换成rng(...)。替换后数值序列肯定会变但脚本行为和可维护性会健康很多。4.4 并行计算里的随机数为什么每个worker不能共用同一个流用parfor、spmd并行时随机数最容易翻车。如果每个 worker 都用同一个全局随机流大家拿到的序列可能完全相同统计结果聚在一起会严重偏离真实分布。所以并行工具箱里每个 worker 默认有自己独立的随机流但想要复现还需要自己管理。我建议用RandStream为每个 worker 显式创建独立但可复现的流。一个常见写法是spmd stream RandStream(mrg32k3a, Seed, 100 labindex, Substream, 1); data rand(stream, 1000, 1); end这里labindex是 worker 编号保证每个 worker 的种子不同mrg32k3a这类生成器支持子流Substream后续继续取数时也不会跟其他流打架。如果你不写这层管理短跑可能没事跑大任务时就容易遇到重复或者相关性异常。记录种子集合也是必须的不然后续想复现并行结果只能重跑一遍成本高得吓人。5. 真随机数怎么拿以及什么时候才该用5.1 为什么软件算法没法产生真随机数任何一个跑在普通CPU上的算法本质上都是一个确定性状态机输入一样状态一样输出一定一样。哪怕把公式写得再复杂只要攻击者拿到足够多输出和算法参数最终都能反推出来。所以“用软件公式生成真随机数”这句话在学术上是矛盾的。真随机数的唯一来源是外部物理世界的不可预测事件。这里容易混的是操作系统随机数接口比如一些系统里的随机设备。它内部维护一个熵池会从键盘按键时间、磁盘中断、硬件噪声等事件收集随机比特再按需输出。严格说它输出的已经不是纯物理零头而是经过密码学提取的随机数据但在工程上如果不关心攻击者能否观测系统熵源它通常被当作高质量随机源来用。5.2 MATLAB里获取接近真随机数的几种办法MATLAB本身没有内置一个可以直接读取硬件熵源的官方函数但有几条实际可用路径。第一条如果你有专门的随机数发生器硬件很多时候它通过USB串口输出字节流你可以在MATLAB里用serialport或read读进来。第二条用Java的 SecureRandom。MATLAB自带Java运行环境可以直接调用sr java.security.SecureRandom; b zeros(16,1,int8); sr.nextBytes(b);这段代码拿到的是密码学安全伪随机数不是纯粹物理真随机但它的不可预测性比普通rand高很多。第三条如果想接外部在线服务需要网络而且要看服务条款我一般不推荐在科研流程里依赖网络。真随机数大多用于给伪随机生成器播种而不是每一次模拟都去外部取数。5.3 什么时候才必须较真用真随机我的判断标准很简单是否有对抗者。如果序列可能被对手观察、预测、利用比如生成密钥、抽奖开奖、游戏装备掉落那就需要用不可预测的随机源如果只是自己模拟、抽样、初始化用伪随机完全够。普通科学家做实验最关心的通常不是不可预测而是分布正确、可复现。这个区别如果不清楚会白白增加很多工程成本。比如给蒙特卡洛模拟接一台量子随机数发生器除了把标题写得更炫酷对数值结果几乎不会有本质提升反而让自己无法复现实验。5.4 混合方案真随机数当种子伪随机数干粗活用工业界最常见做法不是二选一而是组合。用真随机源生成一个不可预测的种子或者生成一段足够长的随机比特作为种子熵池然后把种子喂给伪随机生成器比如rng(seed)后面所有模拟继续用伪随机数。这个方案兼顾了两边的好处种子层有不可预测性数据生成层有速度和可复现性。即使后续伪随机序列理论上可预测攻击者也得先拿到那个随机种子难度比直接预测普通rand高很多。在MATLAB里操作很简单先从SecureRandom读一串字节转成整数种子再传给rng或RandStream。这样每次运行的随机状态都不同但如果你把种子保存下来仍然可以复现。这是我认为科研与工程场景里最实用的“真随机伪随机”组合方式。最后说一点个人习惯。我现在写任何涉及随机数的脚本开头的固定格式一定是明确用rng还是RandStream并把种子写在注释里。这不是强迫症而是被坑过几次后总结出来的随机数本身没bug但用错种子、用错接口、忽略并行流会让结果在发布后变成“薛定谔的复现”。希望这篇内容能帮你少走这些弯路。
RELATED

相关推荐

Codex高效使用指南:15个实战技巧提升开发效率

Codex高效使用指南:15个实战技巧提升开发效率

1. 为什么大多数人用 Codex 只发挥了不到三成实力我见过太多人打开 Codex 之后,输入框里敲一句“帮我写个函数”,然后盯着屏幕等结果,出来一段代码复制走人。这个用法不能说错,但确实浪费了 Codex 最核心的能力。Codex 这类代码生…

📅 2026/10/9 21:13:29
办公自动化 OpenClaw 搭建方案:飞书微信多渠道联动与安装包配置

办公自动化 OpenClaw 搭建方案:飞书微信多渠道联动与安装包配置

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

📅 2026/10/9 21:13:29
数字孪生工厂解决方案:从架构选型到数据联动的实战指南

数字孪生工厂解决方案:从架构选型到数据联动的实战指南

简介:数字孪生(Digital Twin)工厂解决方案文档,面向工厂信息化、智能制造相关技术人员与管理者,针对现代化工厂信息不透明、管理困难等痛点,系统介绍基于三维可视化、快速建模与工业采集网关技术的整体解决…

📅 2026/10/9 21:08:29
MORE NEWS

更多资讯

📰

美术馆预约系统高并发设计与实战避坑指南

简介:本资源为一套完整的美术馆预约系统毕业设计项目源码,面向计算机专业本科生及Web全栈初学者,解决传统美术馆人工预约效率低、信息同步滞后、票务管理粗放等实际问题。压缩包共517个文件,涵盖109个Java后端逻辑文件、77个JavaS…

📰

分离整数各位数字:C++中除法与取模的数学本质

1. 这道题到底在考什么?——从“分离整数的各个数位”看信息学奥赛的底层思维“信息学奥赛一本通 1088:分离整数的各个数 | OpenJudge NOI 1.5 28:分离整数的各个数位”,光看标题,很多人第一反应是:“不就是把一个数字…

📰

区域首席传播官职位拆解:从职责边界到百日实操

最近有几个做公关的朋友在群里转了一条行业新闻,说某国际传播集团任命了一位新的区域首席传播官。大家聊得挺热闹,但多数讨论都停在“谁升职了”“这级别工资多少”这种层面。我觉得挺可惜的,因为这个职位背后藏着一整套关于传播职能转型、跨…

📰

纯AI开发网页小游戏:不用引擎,用HTML+Canvas从零做出一款可玩游戏

最近我做了一件说出去朋友都不太信的事:用AI聊天窗口,从零做了一款叫《蚂蚁搬家》的网页小游戏,全程没打开过Unity、Godot,安装包里连游戏引擎的影子都没有。就是跟AI反复对话,让它写HTML、Canvas和JavaScript代码&…

📰

程序员必懂的组合数工程实践:四种方法选型与落地避坑

1. 这不是数学课,是程序员绕不开的“组合数实战手册”“求组合数”这四个字,乍看像高中数学题,但实际在算法竞赛、密码学实现、概率建模、机器学习特征选择、甚至游戏掉落系统设计中,它从来不是纸上谈兵。我带过的几个模拟项目X里…

📰

Oracle 19c OCP 082备考:带翻译题库与三遍刷题法全解析

简介:这份学习资料瞄准 Oracle 19c OCP 认证考试 1Z0-082 科目,整理为带中文翻译的试题集,内容更新到 2022年5月2日。适合正在备考 OCP 的数据库管理员、开发人员和需要系统认识 Oracle 核心概念的进阶学习者,既能用于刷题自测&am…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬