阿杜伊诺的新手。 "Random" 值在重置时重复
New to arduino. "Random" values are repeating upon reset
我正在 arduino 的基于 C++ 的环境中编写轮盘游戏,该游戏会闪烁 LED 以表示球在盘旋和停止。我的问题是球每次都停在同一个位置。我正在使用 运行domSeed(analogRead(0)) 结合 运行dom(min,max) 生成一个 运行dom 数。这个 运行dom 数字代表我的球 "enters"。但是,当我重置我的 arduino 时,我得到了完全相同的结果。有谁知道我的问题可能出在哪里?
一些重要说明
-我在全局范围内调用 运行dom,因为我想要我的轮盘游戏的单个 运行dom 起点("ball" 进入的地方)。当这两者一起工作时,设置中的 运行domSeed 和全局范围中的 运行dom 会导致问题吗?我不了解 "setup" 如何与 arduino 中的其余代码交互。
-我生成的运行dom号在一个很小的运行ge (0-4)中。但是我已经多次重置我的arduino并且连续30次将2作为第一个数字。
我也试过在全局范围内调用 运行domSeed 而不是设置,但是当我这样做时遇到以下错误。
“
退出状态 1
'(' 标记之前的预期构造函数、析构函数或类型转换
“
如果我可以提供更多详细信息,或者如果有人知道 post 类似问题,请告诉我。
Arduino 代码通常包含 3 个部分。设置、循环和外部。我已经 运行 进行了一些测试,发现当 运行dom 在循环和设置之外时,对 运行domSeed() 的更改不会影响 运行dom 生成的数字。然而,当 运行dom 处于循环中时,它会对来自 运行domSeed 的更改做出反应。这是为什么?当 运行domSeed 在设置中并且 运行dom 在全局中被调用时,有没有办法让 运行dom 和 运行domSeed 一起工作?
以下是我关于如何在 Arduino 或任何微控制器上实现随机数生成器的建议:
仅在启动时为随机数生成器播种(在 Arduino 的 setup()
中)。
使用定时器消耗随机数,and/or空闲时消耗随机数。
这样,即使开机后的状态是可预测的,但状态变化非常迅速,变得不可预测(除非微控制器状态以微秒级的间隔进行非常密切的监控)。
使用异或 (^
) 混合来自至少具有一定随机性的来源的熵,例如 a hardware source based on avalanche noise.
为获得最佳效果,请仅偶尔进行。我会亲自将熵提供给一个单独的伪随机数生成器,并将其输出混合到主生成器状态。
使用人工输入(按钮按下等,通过高分辨率计时器)作为熵的来源。
也就是说,测量例如之间的时间。以非常高的分辨率连续按下按钮,并使用一些最低有效位作为熵来混合到伪随机数生成器状态。
只需按几下按钮,伪随机数生成器的内部状态基本上就变幻莫测了。
使用电池供电的 SRAM(通常作为实时时钟模块的一部分提供)为下一次启动存储种子状态。
从技术上讲,您也可以为此使用 EEPROM 或闪存,但由于它们的写入周期数有限,我不建议为此使用它们。
正如我在其他帖子中提到的,我个人不信任内置的伪随机数生成器,而是实现了一种已知的基于线性反馈移位生成器的生成器(Xorshift 变体或 Mersenne Twister)。它们是众所周知的,并且它们的输出随机性具有特征。
对于基于 Arduino 的游戏,我可能会使用 Xorshift128 作为随机数生成器本身,并使用 Xorshift64* 从硬件源获取熵并使用其输出来扰乱主生成器状态时不时地(例如,按下按钮或类似的场合;每秒少于一次)。
我会确保即使在空闲时,生成器的状态也会提前(通过 "consuming" 伪随机数;只是将它们扔掉),这样物理世界的时间将成为生成序列的主要因素。
各种微控制器具有不同的计时器和可用的硬件功能,因此我要实现的确切代码肯定取决于所使用的硬件。不幸的是,这意味着代码不能跨硬件移植;即使是很小的、看似无害的变化也可能意味着输出变得非常可预测。我可以为 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()
returns 均匀伪随机32位无符号整数,MINIMUM
是函数可以return的最小整数,MINIMUM + LIMIT
是函数最大整数可以return(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()
是return个unsigned int
个统一伪随机数生成器,从0
到UINT_MAX
,包括return个UINT_MAX
个。
我正在 arduino 的基于 C++ 的环境中编写轮盘游戏,该游戏会闪烁 LED 以表示球在盘旋和停止。我的问题是球每次都停在同一个位置。我正在使用 运行domSeed(analogRead(0)) 结合 运行dom(min,max) 生成一个 运行dom 数。这个 运行dom 数字代表我的球 "enters"。但是,当我重置我的 arduino 时,我得到了完全相同的结果。有谁知道我的问题可能出在哪里?
一些重要说明
-我在全局范围内调用 运行dom,因为我想要我的轮盘游戏的单个 运行dom 起点("ball" 进入的地方)。当这两者一起工作时,设置中的 运行domSeed 和全局范围中的 运行dom 会导致问题吗?我不了解 "setup" 如何与 arduino 中的其余代码交互。
-我生成的运行dom号在一个很小的运行ge (0-4)中。但是我已经多次重置我的arduino并且连续30次将2作为第一个数字。
我也试过在全局范围内调用 运行domSeed 而不是设置,但是当我这样做时遇到以下错误。 “ 退出状态 1 '(' 标记之前的预期构造函数、析构函数或类型转换 “
如果我可以提供更多详细信息,或者如果有人知道 post 类似问题,请告诉我。
Arduino 代码通常包含 3 个部分。设置、循环和外部。我已经 运行 进行了一些测试,发现当 运行dom 在循环和设置之外时,对 运行domSeed() 的更改不会影响 运行dom 生成的数字。然而,当 运行dom 处于循环中时,它会对来自 运行domSeed 的更改做出反应。这是为什么?当 运行domSeed 在设置中并且 运行dom 在全局中被调用时,有没有办法让 运行dom 和 运行domSeed 一起工作?
以下是我关于如何在 Arduino 或任何微控制器上实现随机数生成器的建议:
仅在启动时为随机数生成器播种(在 Arduino 的
setup()
中)。
使用定时器消耗随机数,and/or空闲时消耗随机数。
这样,即使开机后的状态是可预测的,但状态变化非常迅速,变得不可预测(除非微控制器状态以微秒级的间隔进行非常密切的监控)。
使用异或 (
^
) 混合来自至少具有一定随机性的来源的熵,例如 a hardware source based on avalanche noise.为获得最佳效果,请仅偶尔进行。我会亲自将熵提供给一个单独的伪随机数生成器,并将其输出混合到主生成器状态。
使用人工输入(按钮按下等,通过高分辨率计时器)作为熵的来源。
也就是说,测量例如之间的时间。以非常高的分辨率连续按下按钮,并使用一些最低有效位作为熵来混合到伪随机数生成器状态。
只需按几下按钮,伪随机数生成器的内部状态基本上就变幻莫测了。
使用电池供电的 SRAM(通常作为实时时钟模块的一部分提供)为下一次启动存储种子状态。
从技术上讲,您也可以为此使用 EEPROM 或闪存,但由于它们的写入周期数有限,我不建议为此使用它们。
正如我在其他帖子中提到的,我个人不信任内置的伪随机数生成器,而是实现了一种已知的基于线性反馈移位生成器的生成器(Xorshift 变体或 Mersenne Twister)。它们是众所周知的,并且它们的输出随机性具有特征。
对于基于 Arduino 的游戏,我可能会使用 Xorshift128 作为随机数生成器本身,并使用 Xorshift64* 从硬件源获取熵并使用其输出来扰乱主生成器状态时不时地(例如,按下按钮或类似的场合;每秒少于一次)。
我会确保即使在空闲时,生成器的状态也会提前(通过 "consuming" 伪随机数;只是将它们扔掉),这样物理世界的时间将成为生成序列的主要因素。
各种微控制器具有不同的计时器和可用的硬件功能,因此我要实现的确切代码肯定取决于所使用的硬件。不幸的是,这意味着代码不能跨硬件移植;即使是很小的、看似无害的变化也可能意味着输出变得非常可预测。我可以为 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()
returns 均匀伪随机32位无符号整数,MINIMUM
是函数可以return的最小整数,MINIMUM + LIMIT
是函数最大整数可以return(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()
是return个unsigned int
个统一伪随机数生成器,从0
到UINT_MAX
,包括return个UINT_MAX
个。