【问题标题】:implementation of rand()rand() 的实现
【发布时间】:2010-11-13 03:03:18
【问题描述】:

我正在用 C 编写一些嵌入式代码,需要使用 rand() 函数。不幸的是,控制器的库中不支持 rand()。我需要一个快速的简单实现,但更重要的是空间开销很小,可以产生相对高质量的随机数。有谁知道使用哪种算法或示例代码?

编辑:它用于图像处理,因此“相对高质量”意味着合适的周期长度和良好的均匀特性。

【问题讨论】:

  • 您在寻找什么,更具体地说?您需要较长的周期长度吗?我们所说的数字有多大(16 位、32 位等等)?它们必须有多随机? “空间开销”是指 ROM 限制、RAM 限制还是两者兼而有之?
  • 如果您有 SysTick 或其他可用于计算从上电到当前时间的时间的东西,那么您可以使用该计数器作为下面显示的一些随机生成器的种子。跨度>
  • 这是 C++ 类中Mersenne Twister 的实现

标签: c random embedded


【解决方案1】:

查看来自 George Marsaglia 的 collection of random number generators。他是随机数生成方面的领先专家,所以我有信心使用他推荐的任何东西。该列表中的生成器很小,有些只需要几个 unsigned long 作为状态。

Marsaglia 的发电机绝对是“高质量”的,以您的长周期和良好均匀分布的标准。他们通过了严格的统计测试,尽管他们不会通过密码学。

【讨论】:

  • 感谢您对此的参考。我做了一些研究,发现"KISS: a Bit Too Simple", by Greg Rose (a) 警告 MWC 中的“坏”种子值和 (b) 对 SHR3 生成器产生怀疑。 This 2003 post by Marsaglia 给出了一个稍微不同的 SHR3,它解决了早期的问题。我很乐意使用 KISS 样式的生成器,只要我检查并避免“坏”种子,并确保我使用的是更好的 SHR3 生成器。
  • Marsaglia 在 90 年代对 PRNG 做出了重大贡献,但是,我认为今天我可能会关注 L'Ecuyer 或 Matsumoto。话虽如此,如果 OP 不在他的嵌入式设备上进行主要模拟,我猜大多数早期的优秀生成器也可以使用 :-)
  • @Joey:Marsaglia 的 KISS 生成器在 L'Ecuyer's TestU01 RNG tests 中仍然表现良好,通过了测试,甚至 L'Ecuyer 自己的一些 RNG(例如 LFSR113)也失败了。
【解决方案2】:

the C code 用于LFSR113 from L'écuyer

unsigned int lfsr113_Bits (void)
{
   static unsigned int z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345;
   unsigned int b;
   b  = ((z1 << 6) ^ z1) >> 13;
   z1 = ((z1 & 4294967294U) << 18) ^ b;
   b  = ((z2 << 2) ^ z2) >> 27; 
   z2 = ((z2 & 4294967288U) << 2) ^ b;
   b  = ((z3 << 13) ^ z3) >> 21;
   z3 = ((z3 & 4294967280U) << 7) ^ b;
   b  = ((z4 << 3) ^ z4) >> 12;
   z4 = ((z4 & 4294967168U) << 13) ^ b;
   return (z1 ^ z2 ^ z3 ^ z4);
}

非常高质量和快速。不要将 rand() 用于任何事情。 比没用还糟糕。

【讨论】:

  • 请注意,它假定为 32 位 int,这可能不适合嵌入式平台。
  • 为什么这是随机的?
  • 我,这取决于你所说的随机。 LFSR RNG's 基于具有已知周期和良好统计特性(感知随机性)的明确定义的数学递归。除非您从某些物理现象(例如线路噪声)中获取熵,否则您使用的任何 RNG 都将是类似的伪随机数。 (密码安全的 RNG 更复杂,但仍然是伪随机的)。更强的 Mersenne Twister 是一个扭曲的 GFSR,所以和上面有关,而更弱的 Xorshift 是 LFSR 的一个特例。 CMWC RNG 可以比其他任何一个都更快、更强大。
  • @me me:它是伪随机的,静态变量保存种子值。它总是返回相同的数字序列。
  • 在答案中将种子设为静态可能是一个错误,但这看起来很有希望,只需确保您在外部提供种子。我会试试看。至于 32 位,实际源使用 uint32_t 而不是 unsigned int,所以这也不应该成为问题。
【解决方案3】:

这是一个指向 ANSI C implementation of a few random number generators 的链接。

【讨论】:

    【解决方案4】:

    我制作了一系列随机数生成器“simplerandom”,它们紧凑且适用于嵌入式系统。该集合在CPython 中可用。

    我到处找了一堆我能找到的简单而体面的东西,然后把它们放在一个小包装里。它们包括几个 Marsaglia 发生器(KISS、MWC、SHR3)和几个 L'Ecuyer LFSR。

    所有生成器都返回一个无符号 32 位整数,并且通常具有由 1 到 4 个 32 位无符号整数组成的状态。

    有趣的是,我发现 Marsaglia 生成器存在一些问题,我已尝试修复/改进所有这些问题。这些问题是:

    • SHR3 发生器(Marsaglia 1999 年 KISS 发生器的组件)损坏。
    • MWC 低 16 位只有大约 229.1 周期。所以我做了一个稍微改进的 MWC,它给低 16 位一个 259.3 周期,这是这个生成器的总周期。

    我发现了一些有关播种的问题,并尝试制定稳健的播种(初始化)程序,因此如果您给它们一个“坏”的种子值,它们也不会中断。

    【讨论】:

    • 这是一篇较旧的帖子,但我想更正一下:Marsaglia 的 2x16 MWC 生成器的低 16 位实际上似乎有 589823999 = 2^29.1357092836584 的周期,而不是 2^16。 (这是可能的,因为有 32 位状态包括进位。)周期是 b mod p 的阶,其中 b = 2^16 和 p = 18000 * 2^16 - 1。阶 = 589823999 = (p - 在这种情况下为 1)/2。使用 base-65536 MWC 和 16 位乘数可以做的最好的事情是周期为 2^30.9990971519312,乘数为 65495。最好的安全素数是 65184,周期为 2^30.9922302642905。
    • Marsaglia 的 2x16 MWC 发生器的高比特周期为 1211400191 = 2^30.1740283979574,低比特周期为 589823999 = 2^29.1357092836584。两者串联起来有一个周期714512905044983809 = 2^59.3097376816159,但是个别高低字还是有独立的低周期。您修改后的 MWC2 在不破坏底层生成器的数学基础的情况下提高了低位的输出质量,使低位的周期与总组合相同,并且也有助于高位......但我不是确定多少。
    • 是的,你说的很对。我说 MWC 低 16 位只有 2^16 周期,但我应该说 2^29.1 周期。我已经相应地对其进行了编辑。但我不认为我在 MWC2 中的更改对高位有任何影响。
    • 您对高位所做的更改是微妙的:当您添加高位和低位时,有时会将另一个 1 位带入高位,从而稍微改变高位一半的时间。实际上,我一开始就搞错了高位的周期:我说它们有一个独立的周期 2^30.1740283979574,但它们实际上有完整的周期 2^59.3097376816159,因为 Marsaglia 已经从添加构建它们znew 的低 16 位和 wnew 的高 16 位。起初我没有抓住后者。
    • 我的印象是您的更改将有助于高位和低位的周期(随机性,不是那么多),但由于高位已经有完整的周期,它没关系。 :p 实际上,我在上一条评论中又犯了一个错误:携带 1 位的发生率远低于一半……只是偶尔。顺便说一句,用 MWC 找到额外的坏种子很好。虽然它们似乎不符合可以推广到其他 MWC 生成器的明显模式,但有点可怕......
    【解决方案5】:

    我推荐 David Carta 的学术论文Two Fast Implementations of the Minimal Standard Random Number Generator。您可以通过 Google 找到免费的 PDF。 Minimal Standard Random Number Generator 的原始论文也值得一读。

    Carta 的代码在 32 位机器上提供快速、高质量的随机数。如需更全面的评估,请参阅论文。

    【讨论】:

      【解决方案6】:

      Mersenne twister

      来自维基百科:

      • 它的设计周期为 219937 - 1(算法的创建者证明了这个属性)。在实践中,几乎没有理由使用更大的周期,因为大多数应用程序不需要 219937 唯一组合(219937 约为 4.3 × 106001;这比可观测宇宙中估计的粒子数(1080)大许多数量级。
      • 它有一个非常高阶的维度均匀分布(参见线性同余生成器)。这意味着输出序列中连续值之间的序列相关性可以忽略不计。
      • 它通过了许多统计随机性测试,包括 Diehard 测试。它通过了大多数(但不是全部)更严格的 TestU01 Crush 随机性测试。

      • 链接上提供了多种语言的源代码。

      【讨论】:

        【解决方案7】:

        我会从 GNU C 库中获取一个,源代码可以在线浏览。

        http://qa.coreboot.org/docs/libpayload/rand_8c-source.html

        但是,如果您对随机数的质量有任何顾虑,您可能应该查看更仔细编写的数学库。这是一个很大的主题,标准的rand 实现并没有得到专家的高度评价。

        这是另一种可能性:http://www.boost.org/doc/libs/1_39_0/libs/random/index.html

        (如果你发现你有太多选择,你总是可以随机选择一个。)

        【讨论】:

        • rand.c 源代码链接似乎已损坏。试试rand.crandom.c 的git repo
        • 请注意,代码将遵循 GPL 或 LGPL。始终了解您的来源的许可证。
        【解决方案8】:

        我发现了这个:Simple Random Number Generation, by John D. Cook

        它应该很容易适应 C,因为它只有几行代码。

        编辑:您可以澄清“相对高质量”的含义。您是为核发射代码生成加密密钥,还是为扑克游戏生成随机数?

        【讨论】:

        • 那是 John D. Cook 写的 that other answer,它基于他在回答中链接到的 Marsaglia 帖子。
        【解决方案9】:

        更好的是,使用多个线性反馈移位寄存器将它们组合在一起。

        假设sizeof(unsigned) == 4:

        unsigned t1 = 0, t2 = 0;
        
        unsigned random()
        {
            unsigned b;
        
            b = t1 ^ (t1 >> 2) ^ (t1 >> 6) ^ (t1 >> 7);
            t1 = (t1 >> 1) | (~b << 31);
        
            b = (t2 << 1) ^ (t2 << 2) ^ (t1 << 3) ^ (t2 << 4);
            t2 = (t2 << 1) | (~b >> 31);
        
            return t1 ^ t2;
        }
        

        【讨论】:

          【解决方案10】:

          标准解决方案是使用linear feedback shift register

          【讨论】:

            【解决方案11】:

            有一个简单的RNG命名为KISS,它是一个根据三个数字生成的随机数。

            /* Implementation of a 32-bit KISS generator which uses no multiply instructions */ 
            
            static unsigned int x=123456789,y=234567891,z=345678912,w=456789123,c=0; 
            
            unsigned int JKISS32() { 
                int t; 
            
                y ^= (y<<5); y ^= (y>>7); y ^= (y<<22); 
            
                t = z+w+c; z = w; c = t < 0; w = t&2147483647; 
            
                x += 1411392427; 
            
                return x + y + w; 
            }
            

            还有一个测试RNG的网站http://www.phy.duke.edu/~rgb/General/dieharder.php

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-02-13
              • 2013-09-09
              • 1970-01-01
              • 2013-10-13
              • 1970-01-01
              • 2011-07-18
              • 1970-01-01
              相关资源
              最近更新 更多