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)为:
pad += dat + d * n,随后进行 3 位左移与 29 位右移的混合(等价于循环左移 3 位);n = pad | 2(保证 n 的奇偶性,维持状态活跃度);d ^= (pad << 31) + (pad >> 1);dat ^= pad ^ (d >> 8) ^ 1;- 最终输出
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}。
理解这一约定是正确使用randrange、randint、random、uniform的前提,下面逐一说明。
整数随机函数
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 > 32或n < 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 <= b时a <= N <= b;当b < a时b <= 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) <= 4与2 <= 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 = seed、yasmarang_n = 69、yasmarang_d = 233、yasmarang_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时才可用。当前仓库中以下端口配置了各自的硬件/系统随机源:
| 端口 | 随机源宏 | 配置文件 |
|---|---|---|
| stm32 | mp_hal_get_hw_random_u32() | ports/stm32/mpconfigport.h |
| esp32 | esp_random() | ports/esp32/mpconfigport.h |
| esp8266 | *WDEV_HWRNG(硬件 RNG) | ports/esp8266/mpconfigport.h |
| rp2 | get_rand_32() | ports/rp2/mpconfigport.h |
| nrf | rng_generate_random_word() | ports/nrf/mpconfigport.h |
| mimxrt | trng_random_u32() | ports/mimxrt/mpconfigport.h |
| samd21 / samd51 | trng_random_u32(300)/trng_random_u32() | ports/samd/mcu/samd21/mpconfigmcu.h、ports/samd/mcu/samd51/mpconfigmcu.h |
| alif | se_services_rand64() | ports/alif/mpconfigport.h |
| webassembly | mp_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_INIT与MICROPY_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 | 是否包含randrange、randint、choice、random、uniform等扩展函数 | py/mpconfig.h |
从源码可以看出:
getrandbits与seed是基础函数,只要MICROPY_PY_RANDOM开启就存在(extmod/modrandom.c);randrange、randint、choice由#if MICROPY_PY_RANDOM_EXTRA_FUNCS包裹(extmod/modrandom.c);random与uniform进一步依赖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 中的shuffle、sample、gauss等高级函数,嵌入式场景下可通过组合randrange、choice自行实现。
实战建议
综合文档、源码与测试,以下实践可以提升代码质量:
- 可复现性优先:测试与演示场景使用固定种子
random.seed(1),保证每次运行序列一致(测试 tests/extmod/random_basic.py 即以此验证确定性); - 真随机需显式播种:在支持硬件 RNG 的端口(如 stm32、esp32、rp2)上调用
random.seed()获取不可预测序列;跨平台代码应捕获ValueError做降级处理; - 注意区间端点:
randrange(stop)是半开区间[0, stop),randint(a, b)是闭区间[a, b],random()是[0.0, 1.0),混用前先对照本文的区间约定; - 边界值防御:
randrange传 0 或空区间、randint传a > b、choice传空序列均会抛异常(ValueError或IndexError),必要时先做输入校验; - 裁剪感知:若目标固件通过
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),仅供参考