news 2026/9/20 6:40:00

MicroPython random 模块完全指南:伪随机数生成、种子管理与跨平台实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MicroPython random 模块完全指南:伪随机数生成、种子管理与跨平台实战

MicroPython random 模块完全指南:伪随机数生成、种子管理与跨平台实战

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

random模块是 MicroPython 内置的伪随机数生成器(PRNG)接口,用于在微控制器和受限系统上生成可复现或不可预测的随机数。本文基于 docs/library/random.rst 官方文档,结合 extmod/modrandom.c 源码实现与 tests/extmod 测试用例,系统讲解其核心函数、区间语义、配置开关、种子机制与底层 Yasmarang 算法原理,帮助你写出可移植、可复现、可测试的随机逻辑代码。

模块概览

random模块实现了一个伪随机数生成器(PRNG)。与 CPython 的random模块(见 CPython 对应文档)不同,MicroPython 的实现针对嵌入式环境做了极致的精简:核心状态仅 4 个变量(3 个 32 位整数加 1 个字节),整个生成器基于著名的 Yasmarang 算法,由 Ilya Levin 设计、以 Public Domain 授权,源码位于 extmod/modrandom.c。

Yasmarang 的核心更新流程(extmod/modrandom.c)为:

  1. pad += dat + d * n,随后进行 3 位左移与 29 位右移的混合(等价于循环左移 3 位);
  2. n = pad | 2(保证 n 的奇偶性,维持状态活跃度);
  3. d ^= (pad << 31) + (pad >> 1)
  4. dat ^= pad ^ (d >> 8) ^ 1
  5. 最终输出pad ^ (d << 5) ^ (pad >> 18) ^ (dat << 1)

该算法以极小的内存占用换取可接受的随机质量,非常适合 Flash/RAM 受限的 MCU 场景。模块本身通过MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_random, mp_module_random)注册(extmod/modrandom.c),支持在运行时以冻结模块等方式扩展。

区间符号约定

文档与源码中的函数都基于两种区间:

  • 开区间():不含端点。例如(0, 1)表示大于 0 且小于 1,集合记法为(0, 1) = {x | 0 < x < 1}
  • 闭区间[]:包含端点。例如[0, 1]表示大于等于 0 且小于等于 1,集合记法为[0, 1] = {x | 0 <= x <= 1}

理解这一约定是正确使用randrangerandintrandomuniform的前提,下面逐一说明。

整数随机函数

getrandbits(n)

返回一个包含n个随机位的整数,取值范围为0 <= n <= 32

import random # 生成一个 16 位随机整数(0 ~ 65535) x = random.getrandbits(16) # 生成一个 32 位随机整数(0 ~ 4294967295) y = random.getrandbits(32)

源码 extmod/modrandom.c 中的实现要点:

  • n > 32n < 0时抛出ValueError(错误消息为"bits must be 32 or less");
  • n == 0时直接返回 0;
  • 通过mask >>= (32 - n)构造掩码,再与 Yasmarang 输出做与运算,取低 n 位。

测试 tests/extmod/random_basic.py 验证了对于1, 2, 3, 4, 16, 32等位数,返回值始终满足getrandbits(b) < (1 << b),即严格落在指定位数范围内。

randint(a, b)

返回闭区间[a, b]内的随机整数(含两端点),要求a <= b

import random # 模拟掷骰子:1 到 6 之间的整数 dice = random.randint(1, 6) # 支持负数 n = random.randint(-2, 2)

实现见 extmod/modrandom.c:当a > b时抛出ValueError。底层通过a + yasmarang_randbelow(b - a + 1)实现,即先计算区间长度b - a + 1,再生成小于该长度的偏移量。

randrange 三种调用形式

randrange与 CPython 的range参数语义一致,支持三种形式:

import random # 形式一:randrange(stop),返回 [0, stop) 内的随机整数 x = random.randrange(10) # 0 ~ 9 # 形式二:randrange(start, stop),返回 [start, stop) 内的随机整数 y = random.randrange(2, 6) # 2, 3, 4, 5 # 形式三:randrange(start, stop[, step]),按 step 步长返回 z = random.randrange(1, 10, 2) # 奇数:1, 3, 5, 7, 9

文档特别强调:randrange(1, 10, 2)将返回 1 到 9 之间的奇数(含 1 和 9)。测试 tests/extmod/random_extra.py 断言random.randrange(1, 9, 2) in (1, 3, 5, 7),与文档语义一致。

源码实现(extmod/modrandom.c)的核心逻辑:

  • randrange(stop)stop > 0时返回yasmarang_randbelow(stop),否则报错;
  • randrange(start, stop):要求start < stop,返回start + yasmarang_randbelow(stop - start)
  • randrange(start, stop, step):先根据 step 正负计算元素个数n = (stop - start + step - 1) / step(step>0 时)或n = (stop - start + step + 1) / step(step<0 时),n > 0时返回start + step * yasmarang_randbelow(n)
  • step 为 0 或区间为空时抛出ValueError

测试 tests/extmod/random_extra.py 覆盖了空区间(randrange(0)randrange(2, 1)randrange(2, 1, 1))与零步长(randrange(2, 1, 0))等边界情况,均抛出ValueError

浮点随机函数

浮点函数依赖MICROPY_PY_BUILTINS_FLOAT配置,仅当构建启用浮点支持时可用(见 extmod/modrandom.c 的条件编译)。

random()

返回[0.0, 1.0)范围内的随机浮点数,即大于等于 0 且小于 1。

import random # 生成 0.0 ~ 1.0 之间的随机浮点数(不含 1.0) p = random.random()

其实现yasmarang_float()(extmod/modrandom.c)通过构造 IEEE 754 浮点数的符号位、指数位与尾数位实现:符号为 0,指数为偏置值,尾数填充 Yasmarang 输出(若尾数位宽大于 32 则拼接两次 32 位输出),最后减 1 使结果落在 [0, 1)。测试 tests/extmod/random_extra_float.py 对 50 次调用断言0 <= random.random() < 1

uniform(a, b)

返回浮点数N,满足:当a <= ba <= N <= b;当b < ab <= N <= a。即无论参数顺序,返回值总落在两端点之间。

import random # 返回 0.0 ~ 4.0 之间的浮点数 v = random.uniform(0, 4) # 参数逆序同样有效:返回值仍在 -2.0 ~ 2.0 之间 w = random.uniform(2, -2)

实现为a + (b - a) * yasmarang_float()(extmod/modrandom.c)。测试 tests/extmod/random_extra_float.py 验证了0 <= random.uniform(0, 4) <= 42 <= random.uniform(2, 6) <= 6等断言。

种子与状态管理

seed(n=None, /)

初始化随机数生成器的种子:

import random # 使用固定种子,得到可复现的随机序列 random.seed(42) print(random.getrandbits(16)) # 每次运行结果一致 # 不传参数或传 None:使用硬件真随机数播种(若端口支持) random.seed()

源码 extmod/modrandom.c 展示了播种的底层细节:

  • 显式传入整数n时,seed被截断为 32 位(mp_obj_get_int_truncated),随后重置四个状态变量:yasmarang_pad = seedyasmarang_n = 69yasmarang_d = 233yasmarang_dat = 0
  • 不传参或传None时,若端口定义了MICROPY_PY_RANDOM_SEED_INIT_FUNC宏则调用它获取随机源,否则抛出ValueError(错误消息"no default seed")。

测试 tests/extmod/random_basic.py 验证了 PRNG 的可复现性:seed(1)之后两次getrandbits(16)结果相同;同时验证seed(0)后首次数值非零。

无参 seed() 的硬件随机源

seed()的无参形式只有在端口启用MICROPY_PY_RANDOM_SEED_INIT_FUNC时才可用。当前仓库中以下端口配置了各自的硬件/系统随机源:

端口随机源宏配置文件
stm32mp_hal_get_hw_random_u32()ports/stm32/mpconfigport.h
esp32esp_random()ports/esp32/mpconfigport.h
esp8266*WDEV_HWRNG(硬件 RNG)ports/esp8266/mpconfigport.h
rp2get_rand_32()ports/rp2/mpconfigport.h
nrfrng_generate_random_word()ports/nrf/mpconfigport.h
mimxrttrng_random_u32()ports/mimxrt/mpconfigport.h
samd21 / samd51trng_random_u32(300)/trng_random_u32()ports/samd/mcu/samd21/mpconfigmcu.h、ports/samd/mcu/samd51/mpconfigmcu.h
alifse_services_rand64()ports/alif/mpconfigport.h
webassemblymp_js_random_u32()ports/webassembly/mpconfigport.h
unix(变体)mp_random_seed_init()ports/unix/variants/mpconfigvariant_common.h

以 stm32 端口为例,MICROPY_PY_RANDOM_SEED_INIT_FUNC位于#if MICROPY_HW_ENABLE_RNG条件内,即只有启用了硬件 RNG 外设的板卡才支持无参seed()。因此,跨平台代码若需真随机种子,应先探测seed(None)是否抛出ValueError

导入时自动播种(模块init

当构建同时启用了MICROPY_MODULE_BUILTIN_INITMICROPY_PY_RANDOM_SEED_INIT_FUNC时,SEED_ON_IMPORT被置 1(extmod/modrandom.c),此时:

  • 模块的四个状态变量放在 BSS 段(初始为 0),而非预置pad=0xeda4baba, n=69, d=233的固定初值;
  • 模块导入时自动执行mod_random___init__,通过static bool seeded标志保证只播种一次(即使以多个名字导入,extmod/modrandom.c);
  • 模块全局表中会多出__init__一项(extmod/modrandom.c)。

这意味着在支持硬件随机源的端口上,import random后直接调用函数即可获得不可预测的序列;而在不支持硬件随机源的构建中,状态使用固定初值,多次运行同一程序会得到完全相同的序列,此时应显式调用random.seed(...)获取期望的随机性。

其他函数

choice(sequence)

从序列中随机选取并返回一个元素,序列可以是元组、列表或任何支持下标操作的对象:

import random # 从列表中随机选一个 item = random.choice(['red', 'green', 'blue']) # 从字符串中随机选一个字符 char = random.choice("hello")

实现 extmod/modrandom.c 先通过mp_obj_len(seq)获取长度,再调用yasmarang_randbelow(len)生成下标并以下标操作取值;空序列抛出IndexError。测试 tests/extmod/random_extra.py 验证了对[1, 2, 5, 6]的 50 次选择结果均在列表中,且choice([])抛出IndexError

配置开关与裁剪

random模块的功能受py/mpconfig.h中两个宏控制(默认值均关联MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES,即标准以上构建默认启用):

配置宏作用默认值定义位置
MICROPY_PY_RANDOM是否编译整个 random 模块py/mpconfig.h
MICROPY_PY_RANDOM_EXTRA_FUNCS是否包含randrangerandintchoicerandomuniform等扩展函数py/mpconfig.h

从源码可以看出:

  • getrandbitsseed是基础函数,只要MICROPY_PY_RANDOM开启就存在(extmod/modrandom.c);
  • randrangerandintchoice#if MICROPY_PY_RANDOM_EXTRA_FUNCS包裹(extmod/modrandom.c);
  • randomuniform进一步依赖MICROPY_PY_BUILTINS_FLOAT(extmod/modrandom.c),构建时未启用浮点则不可用;
  • yasmarang_randbelow辅助函数同样只在MICROPY_PY_RANDOM_EXTRA_FUNCS下编译(extmod/modrandom.c)。

因此在裁剪固件时,若 Flash 空间紧张,可仅保留基础函数;若需randint等便捷接口,则必须确保MICROPY_PY_RANDOM_EXTRA_FUNCS为 1。

与 CPython 的差异

random模块的接口设计对齐 CPython,但存在明显差异,这些差异被仓库的 cpydiff 测试显式记录:

  • getrandbits 位宽上限:MicroPython 的getrandbits最多返回 32 位,超过会抛ValueError;而 CPython 无此限制(见 tests/cpydiff/modules_random_getrandbits.py)。文档与源码一致声明0 <= n <= 32
  • randint 取值范围:MicroPython 的randint返回值受原生字长限制,超大整数会失败(见 tests/cpydiff/modules_random_randint.py)。

对于需要超过 32 位随机整数或超大整数区间的场景,文档给出的工作方向是使用 micropython-lib 中更完整的 random 模块实现。

另外,MicroPython 的random不支持 CPython 中的shufflesamplegauss等高级函数,嵌入式场景下可通过组合randrangechoice自行实现。

实战建议

综合文档、源码与测试,以下实践可以提升代码质量:

  1. 可复现性优先:测试与演示场景使用固定种子random.seed(1),保证每次运行序列一致(测试 tests/extmod/random_basic.py 即以此验证确定性);
  2. 真随机需显式播种:在支持硬件 RNG 的端口(如 stm32、esp32、rp2)上调用random.seed()获取不可预测序列;跨平台代码应捕获ValueError做降级处理;
  3. 注意区间端点randrange(stop)是半开区间[0, stop)randint(a, b)是闭区间[a, b]random()[0.0, 1.0),混用前先对照本文的区间约定;
  4. 边界值防御randrange传 0 或空区间、randinta > bchoice传空序列均会抛异常(ValueErrorIndexError),必要时先做输入校验;
  5. 裁剪感知:若目标固件通过MICROPY_PY_RANDOM_EXTRA_FUNCS=0或关闭浮点裁剪了功能,需改用getrandbits/seed自行实现等价逻辑。

参考资源

  • 官方模块文档:docs/library/random.rst
  • 核心实现源码:extmod/modrandom.c
  • 配置宏定义:py/mpconfig.h
  • 基础功能测试:tests/extmod/random_basic.py
  • 扩展整数功能测试:tests/extmod/random_extra.py
  • 扩展浮点功能测试:tests/extmod/random_extra_float.py
  • 与 CPython 差异说明:tests/cpydiff/modules_random_getrandbits.py、tests/cpydiff/modules_random_randint.py
  • 各端口硬件随机源配置示例:ports/stm32/mpconfigport.h、ports/esp32/mpconfigport.h、ports/rp2/mpconfigport.h

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 6:39:54

联想平板刷机全攻略:刷机包、救砖与降级实操指南

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

作者头像 李华
网站建设 2026/9/20 6:39:17

SpringBoot+Vue少儿编程管理系统开发实践

1. 项目背景与核心价值少儿编程教育近年来呈现爆发式增长态势&#xff0c;根据行业调研数据显示&#xff0c;2023年国内少儿编程市场规模已突破百亿元&#xff0c;年复合增长率保持在30%以上。在这个背景下&#xff0c;教育机构对专业化管理系统的需求日益迫切。传统的人工排课…

作者头像 李华
网站建设 2026/9/20 6:39:10

交易风控核心:回撤限制原理与实战技巧

1. 回撤限制的本质&#xff1a;交易员的生存法则在交易这个残酷的竞技场里&#xff0c;回撤限制就像氧气面罩上的压力阀——它不会让你飞得更高&#xff0c;但能确保你不会突然窒息。我见过太多交易员沉迷于寻找"圣杯策略"&#xff0c;却忽略了最基本的生存技能&…

作者头像 李华
网站建设 2026/9/20 6:38:29

AI测试用例生成流水线:知识库+工作流解决AI失忆问题

先说一个让我头疼很久的现象&#xff1a;AI 辅助测试用例生成这件事&#xff0c;单看第一轮效果都还不错&#xff0c;但只要需求稍微复杂一点——涉及历史缺陷、业务规则、字段边界、上下游依赖——模型就开始“失忆”。不是漏掉关键前置条件&#xff0c;就是一本正经地编造不存…

作者头像 李华
网站建设 2026/9/20 6:38:27

大模型推理显存怎么算?从参数量到KV Cache的完整计算公式

前两天群里又有人拿着一张 8G 显存的卡问&#xff1a;能不能本地跑 7B 的 LLM&#xff1f;这个问题如果只回答"能"或者"不能"&#xff0c;那基本等于没答——同样是 7B&#xff0c;FP16 半精度加载权重就要吃掉 14GB&#xff0c;INT4 量化后才 4GB 出头&am…

作者头像 李华
网站建设 2026/9/20 6:37:27

增量式PID原理与MATLAB仿真:参数整定、抗饱和与C移植

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

作者头像 李华