以下是我对如何在 Arduino 或任何微控制器上实现随机数生成器的建议:
仅在启动时播种随机数生成器(在 Arduino 中为 setup())。
-
使用计时器来消耗随机数,和/或在空闲时消耗随机数。
这样,即使上电后的状态是可预测的,但状态变化非常迅速,并且变得不可预测(除非以微秒间隔非常密切地监控微控制器状态)。
-
使用异或 (^) 混合来自至少具有一定随机性的源的熵,例如 a hardware source based on avalanche noise。
为获得最佳效果,请仅偶尔进行。我会亲自将熵提供给单独的伪随机数生成器,并将其输出混合到主生成器状态。
-
使用人工输入(按钮按下等,通过高分辨率计时器)作为熵的来源。
也就是说,测量例如之间的时间。以非常高分辨率连续按下按钮,并使用一些最低有效位作为熵来混合到伪随机数生成器状态。
只需按几下按钮,伪随机数生成器的内部状态基本上就无法预测了。
-
使用电池供电的 SRAM(通常作为实时时钟模块的一部分提供)为下次启动存储种子状态。
从技术上讲,您也可以为此使用 EEPROM 或闪存,但由于它们的写入周期数有限,因此我不建议使用它们。
正如我在其他帖子中提到的,我个人不相信内置的伪随机数生成器,而是实现了一种已知的基于线性反馈移位生成器的生成器(Xorshift 变体或 Mersenne Twister)。它们是众所周知的,并且它们的输出随机性具有特征。
对于基于 Arduino 的游戏,我可能会使用 Xorshift128 作为随机数生成器本身,使用 Xorshift64* 从硬件源获取熵并使用其输出不时扰乱主生成器状态(例如,在按钮按下或类似情况下;每秒少于一次)。
我会确保即使在空闲时,生成器的状态也会提前(通过“消耗”伪随机数;只是将它们扔掉),因此物理世界的时间将成为生成序列的主要因素。
各种微控制器都有不同的计时器和可用的硬件功能,所以我要实现的确切代码肯定取决于所使用的硬件。不幸的是,这意味着代码不能跨硬件移植。即使是很小的、看似无害的变化也可能意味着输出变得非常可预测。我可以为 Arduino Leonardo / Arduino Pro Micro(均基于 ATmega32u4 微控制器)编写一个示例,因为我手头有一个,但如果您使用其他微控制器,代码可能很有趣,但如果您尝试在不知道其细节和行为的情况下将其移植到另一个硬件架构。
要生成一个范围内的伪随机整数,我建议使用排除方法,而不是模 (%) 方法。排除法确保均匀分布;取模方法可以对范围较小端附近的某些值产生小的偏差。
排除法的思想很简单。您准确获取所需的位数以覆盖范围,但排除范围之外的值。平均而言,您可能会消耗多达两倍的随机数位,但使用像 Xorshift 这样的快速生成器,这绝对不是问题。
固定 32 位有符号整数范围函数的一般模式是
static inline int32_t rndrange(void)
{
uint32_t u;
do {
u = random32() >> SHIFT;
} while (u > LIMIT);
return u + MINIMUM;
}
其中random32() 返回统一的伪随机 32 位无符号整数,MINIMUM 是函数可以返回的最小整数,MINIMUM + LIMIT 是函数可以返回的最大整数 (LIMIT = MAXIMUM - MINIMUM),SHIFT 是
31 if LIMIT == 1 ║ 15 LIMIT <= 131071
30 LIMIT <= 3 ║ 14 LIMIT <= 262143
29 LIMIT <= 7 ║ 13 LIMIT <= 524287
28 LIMIT <= 15 ║ 12 LIMIT <= 1048575
27 LIMIT <= 31 ║ 11 LIMIT <= 2097151
26 LIMIT <= 63 ║ 10 LIMIT <= 4194303
25 LIMIT <= 127 ║ 9 LIMIT <= 8388607
24 LIMIT <= 255 ║ 8 LIMIT <= 16777215
23 LIMIT <= 511 ║ 7 LIMIT <= 33554431
22 LIMIT <= 1023 ║ 6 LIMIT <= 67108863
21 LIMIT <= 2047 ║ 5 LIMIT <= 134217727
20 LIMIT <= 4095 ║ 4 LIMIT <= 268435455
19 LIMIT <= 8191 ║ 3 LIMIT <= 536870911
18 LIMIT <= 16383 ║ 2 LIMIT <= 1073741823
17 LIMIT <= 32767 ║ 1 LIMIT <= 2147483647
16 LIMIT <= 65535 ║ 0 if LIMIT <= 4294967295
在 Arduino 或任何使用 GCC 编译的 C 或 C++ 代码中,您可以使用
static inline int random_intrange(const int minval,
const int maxval)
{
if (maxval > minval) {
const unsigned int limit = maxval - minval;
const unsigned char shift = __builtin_clz(limit);
unsigned int u;
do {
u = random_unsigned_int() >> shift;
} while (u > limit);
return minval + u;
} else
return minval;
}
static inline unsigned int random_uintrange(const unsigned int minval,
const unsigned int maxval)
{
if (maxval > minval) {
const unsigned int limit = maxval - minval;
const unsigned char shift = __builtin_clz(limit);
unsigned int u;
do {
u = random_unsigned_int() >> shift;
} while (u > limit);
return minval + u;
} else
return minval;
}
只要random_unsigned_int() 是一个统一的伪随机数生成器,它返回unsigned ints,从0 到UINT_MAX,包括。