【问题标题】:c rand() function and ISAAC random number generatorc rand() 函数和 ISAAC 随机数生成器
【发布时间】:2016-01-05 16:12:55
【问题描述】:

我用 C 语言编写了一个程序,它使用了许多不同的随机数生成器,其中一个是 ISAAC(可在 http://burtleburtle.net/bob/rand/isaacafa.html 获得)。它运作良好,但问题在于 rand.h rand() 被重新定义为宏。在我的程序中,我也想使用标准的 C rand() 函数。我尝试将宏的名称更改为 rand12() 但我在 ISAAC 中看不到调用该宏的任何其他位置,因此这不起作用。

您能否提供一些想法,我可以如何保留标准 rand() 函数并同时使用 ISAAC?

【问题讨论】:

  • rand 宏翻译成什么?

标签: c random


【解决方案1】:

鉴于标题 rand.h 包含:

#ifndef STANDARD
#include "standard.h"
#endif

#ifndef RAND
#define RAND
#define RANDSIZL   (8)
#define RANDSIZ    (1<<RANDSIZL)

/* context of random number generator */
struct randctx
{
  ub4 randcnt;
  ub4 randrsl[RANDSIZ];
  ub4 randmem[RANDSIZ];
  ub4 randa;
  ub4 randb;
  ub4 randc;
};
typedef  struct randctx  randctx;

/* If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed. */
void randinit(/*_ randctx *r, word flag _*/);

void isaac(/*_ randctx *r _*/);

/* Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value */
#define rand(r) \
   (!(r)->randcnt-- ? \
     (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
     (r)->randrsl[(r)->randcnt])

#endif  /* RAND */

您将需要对代码做一些工作,以便能够与来自&lt;stdlib.h&gt;rand() 一起使用它。 ISAAC rand() 的接口与&lt;stdlib.h&gt;rand() 的接口也不同。

为自己创建一个新标头"isaac.h",它定义了处理 ISAAC 系统特性的覆盖函数。

也许,如果您不打算在线程上下文中工作

#ifndef ISAAC_H_INCLUDED
#define ISAAC_H_INCLUDED

extern void isaac_init(unsigned long seed);
extern int  isaac_rand(void);

#endif

然后您在isaac.c 中实现这些函数,以便它们调用rand.h 中定义的函数,并且isaac_rand() 包含来自rand.hrand() 宏的调用(从某处提供上下文,这是非螺纹部分的来源)。您可以决定如何处理seed,或者是否更改种子机制。

然后您可以在代码中使用isaac_init()isaac_rand() 函数,以及普通的rand()srand()

我还将升级rand.h 中的代码,为包中的功能提供完整的原型。注释原型是 90 年代中期首次编写时遗留下来的,当时标准 C 编译器还没有被普遍使用。标题中最早的日期是 1996 年;这正处于标准 C 编译器几乎普遍可用的风口浪尖。

我注意到标题中的 cmets(上面已删除)表示代码在公共域中;这意味着进行您需要的任何修改都是 100% 合法的。


isaac.c

#include "isaac.h"
#include "rand.h"

static randctx control;

void isaac_init(unsigned long seed)
{
    assert(seed != 0);
    randinit(&control, FALSE);
}

int isaac_rand(void)
{
    return rand(&control);
}

此实现忽略了您提供的种子,主要是因为该结构需要八个 32 位数字来为上下文结构的 randrsl 成员(我称为 control)提供种子。你可以做一些事情,比如连续使用种子值 8 次而不是完全忽略它,或者每次都添加一些数字,或者任何其他更复杂的播种技术。您应该认真考虑使用/dev/urandom 作为种子的来源:

#define DEV_URANDOM "/dev/urandom"

int ur = open(DEV_URANDOM, O_RDONLY);
if (ur >= 0)
{
    read(ur, control.randrsl, sizeof(control.randrsl));
    close(ur);
}

在调用randinit() 之前将此代码放入isaac_init(),并将FALSE 更改为TRUE。您可能还会丢失 seed 函数的 seed 参数。

这给您带来了跟踪随机种子以获得可重复性的问题(这在调试时可能很重要)。不过,这是由你来解决的——有多种方法可以做到这一点。您可能有两个初始化函数:void isaac_init(void)void isaac_rsl(unsigned int *rsl),它们接受一个包含 8 个unsigned int(或ub4)值的数组并将其用作种子而不是/dev/urandom 的输出。或者您可以传递一个空指针来表示“使用来自/dev/urandom 的输出”,传递一个非空指针来表示“使用我提供的值”。等等。

【讨论】:

  • 非常感谢您的及时回复。我明白你在说什么,但我的问题是 ISAAC 的作者也调用了他的函数 rand 但如果我查看 rand.h 和 standard.h 则在其他任何地方都没有调用 rand (所以如果我只是重命名它,我不能找到我需要更改名称的代码的其他部分)。有一个包含许多组件的 rand 结构,但仅此而已。结果,我不知道要编辑什么。
  • 严格来说,他有一个名为rand() 的宏,它调用一个函数isaac() 并在randctx 结构内四处寻找。一件好事是宏没有链接,因此不包含 rand.h 标头的代码将永远看不到它,并且您的包装器代码将对外界隐藏宏。只有包装器代码使用了名称错误的rand() 宏。请参阅答案中的更新信息。
猜你喜欢
  • 2015-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多