返回介绍

19.1 伪随机数生成器的例子

发布于 2025-02-22 14:00:47 字数 2385 浏览 0 评论 0 收藏 0

如果我们需要 0~1 的随机浮点数,最简单的方法就是用 PRNG(伪随机数发生器),比如马特赛特旋转演算法可以生成一个随机的 32 位的 DWORD。然后我们可以把这个值转为 FLOAT 类型,然后除以 RAND_MAX(我们的例子是 0xFFFFFFFF),这样,我们得到的将是 0..1 区间的数。 但是如我们所知道的是,除法很慢。我们是否能摆脱它呢?就像我们用乘法做除法一样(14 章)。 让我们想想浮点数由什么构成:符号位、有效数字位、指数位。我们只需要在这里面存储一些随机的位就好了。 指数不能变成 0(在本例里面数字会不正常),所以我们存储 0111111 到指数里面,这意味着指数位将是 1。然后,我们用随机位填充有效数字位,然后把符号位设置为 0(正数)。生成的数字将在 1-2 的间隔中生成,所以我们必须从里面再减去 1。 我例子里面是最简单的线性同余随机数生成器,生成 32 位(译注:32-bit 比特位,非数字位)的数字。PRNG 将会用 UNIX 时间戳来初始化。 然后,我们会把 float 类型当作联合体(union)来处理,这是一个 C/C++的结构。它允许我们把一片内存里面各种不同类型的数据联合覆盖到一起用。在我们的例子里,我们可以创建一个 union,然后通过 float 或者 uint32_t 来访问它。因此,这只是一个小技巧,而且是很脏的技巧。

#!cpp
#include <stdio.h>
#include <stdint.h>
#include <time.h>
union uint32_t_float
{
    uint32_t i;
    float f;
};
// from the Numerical Recipes book
const uint32_t RNG_a=1664525;
const uint32_t RNG_c=1013904223;
int main()
{
    uint32_t_float tmp;
    uint32_t RNG_state=time(NULL); // initial seed
    for (int i=0; i<100; i++)
    {
        RNG_state=RNG_state*RNG_a+RNG_c;
        tmp.i=RNG_state & 0x007fffff | 0x3F800000;
        float x=tmp.f-1;
        printf ("%f
", x);
    };
    return 0;
};

清单 19.1: MSVC 2010 (/Ox)

#!bash
$SG4232 DB ’%f’, 0aH, 00H
__real@3ff0000000000000 DQ 03ff0000000000000r ; 1
tv140= -4 ; size = 4
_tmp$= -4 ; size = 4
_main PROC
    push ebp
    mov ebp, esp
    and esp, -64 ; ffffffc0H
    sub esp, 56 ; 00000038H
    push esi
    push edi
    push 0
    call __time64
    add esp, 4
    mov esi, eax
    mov edi, 100 ; 00000064H
$LN3@main:
    ; let’s generate random 32-bit number
    imul esi, 1664525 ; 0019660dH
    add esi, 1013904223 ; 3c6ef35fH
    mov eax, esi
    ; leave bits for significand only
    and eax, 8388607 ; 007fffffH
    ; set exponent to 1
    or eax, 1065353216 ; 3f800000H
    ; store this value as int
    mov DWORD PTR _tmp$[esp+64], eax
    sub esp, 8
    ; load this value as float
    fld DWORD PTR _tmp$[esp+72]
    ; subtract one from it
    fsub QWORD PTR __real@3ff0000000000000
    fstp DWORD PTR tv140[esp+72]
    fld DWORD PTR tv140[esp+72]
    fstp QWORD PTR [esp]
    push OFFSET $SG4232
    call _printf
    add esp, 12 ; 0000000cH
    dec edi
    jne SHORT $LN3@main
    pop edi
    xor eax, eax
    pop esi
    mov esp, ebp
    pop ebp
    ret 0
_main ENDP
_TEXT ENDS
END

GCC 也生成了非常相似的代码。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文