使用rand()在(a,b),[a,b),(a,b]和[a,b]上生成均匀分布的浮点数

Dr.*_* II 4 c random

我想收集"最佳"方法,在一个地方生成所有四种类型间隔的随机数.我厌倦了谷歌搜索这个.搜索结果出现了很多垃圾.甚至相关的结果都是页面或博客,这些页面或博客通常都是错误的,或者讨论过自我指定的专家在某些技术性方面彼此不同意,通常他们的"答案"似乎暴露出他们不了解不同的类型(关闭)间隔开放,半开放.我厌倦了阅读关于在C中为这样一个"简单"问题生成随机数的不良信息.

请告诉我如何生成均匀分布的浮点数.这是我在(a,b),[a,b),(a,b]和[a,b]上的典型方式(使用"long double"作为例子):

long double a=VALUE1,b=VALUE2;
long double x1,x2,x3,x4;

srand((unsigned)time(NULL));

/* x1 will be an element of [a,b] */
x1=((long double)rand()/RAND_MAX)*(b-a) + a;

/* x2 will be an element of [a,b) */
x2=((long double)rand()/((long double)RAND_MAX+1))*(b-a) + a;

/* x3 will be an element of (a,b] */
x3=(((long double)rand()+1)/((long double)RAND_MAX+1))*(b-a) + a;

/* x4 will be an element of (a,b) */    
x4=(((long double)rand()+1)/((long double)RAND_MAX+2))*(b-a) + a;
Run Code Online (Sandbox Code Playgroud)

对于单位间隔(0,1),[0,1),(0,1]和[0,1]的特殊情况:

long double x1,x2,x3,x4;

srand((unsigned)time(NULL));

/* x1 will be an element of [0,1] */
x1=((long double)rand()/RAND_MAX);

/* x2 will be an element of [0,1) */
x2=((long double)rand()/((long double)RAND_MAX+1));

/* x3 will be an element of (0,1] */
x3=(((long double)rand()+1)/((long double)RAND_MAX+1));

/* x4 will be an element of (0,1) */    
x4=(((long double)rand()+1)/((long double)RAND_MAX+2));
Run Code Online (Sandbox Code Playgroud)

我相信RAND_MAX和rand()的返回值的转换是必要的,不仅因为我们想要避免整数除法,而且因为它们是整数,否则添加一个(或两个)可能会溢出它们.

我认为"double"和"float"的版本完全相同,只是替换了类型.对于不同的浮点类型,是否存在任何细微之处?

你看到上述实现有什么问题吗?如果是这样,你会怎样以及如何解决它?

编辑:以上实现通过必要的测试,以确保它们是正确的(至少在运行64位Linux的64位Intel Core 2 Duo机器上):x1可以生成0和1,x2可以生成0但是还没有看到生成1,x3可以生成1,但是没有看到生成0,并且还没有看到x4生成0或1.

Ste*_*sop 6

如果你想要范围内的每一个双精度,概率与它和它的相邻双精度值之间的差值成比例,那么它实际上非常难.

考虑范围[0, 1000].在该范围的非常微小的第一部分中存在绝对的桶值:在0和之间的百万个1000000*DBL_MIN,并且DBL_MIN大约是2*10 -308.2^32在范围内总共有多个值,所以显然一个调用rand()不足以生成它们.您需要做的是均匀地生成双精度的尾数,并选择具有指数分布的指数,然后稍微捏一下以确保结果在范围内.

如果你要求范围内每双是可能的,然后打开和关闭范围之间的差别是相当无关紧要的,因为在一个"真正的"连续均匀随机分布,概率任何发生的确切值是0呢.所以你不妨在开放范围内生成一个数字.

所有这些都说:是的,您提出的实现会生成您所说的范围内的值,对于封闭范围和半封闭范围,它们会以概率1/(RAND_MAX+1)左右生成端点.这对于许多或大多数实际目的来说已经足够了.

只要能够准确表示RAND_MAX+2的范围,您double就可以摆弄+1和+2作品.这对于IEEE双精度和32位是正确的int,但它实际上并不是由C标准保证的.

(我忽略了你的使用,long double因为它混淆了一些东西.它保证至少和它一样大double,但有一些常见的实现与它完全相同double,所以long除了不确定性之外不会添加任何东西).

  • 它在实践中很好,在任何"普通"系统上,`double`比'int`有比特有更多的精度.非正常系统可能包括像ILP64大型机这样的异常架构,其中`int`是64位.因此,如果您想要非常谨慎,那么您需要记录和/或断言对实现细节的确切要求.如果您正在测量来自测量设备的数据并且您非常关心那么我认为您希望它根据设备的粒度进行分配,而不是统一而不是根据`(长双)的粒度(b) - a)/ RAND_MAX`. (2认同)
  • 另一个优点.我的问题的解决方案似乎比我所说的黑白方式更复杂.你的答案可能和一个人一样好.如果没有人在一两周内回答得更好,我会很满意接受你的回复.谢谢你的时间. (2认同)
  • 顺便说一下这个要求很简单.你只需要`((双)RAND_MAX <(双)RAND_MAX + 1)&&((双)RAND_MAX + 1 <(双)RAND_MAX + 2)`以确保调整确实排除了终点而不是没有差别.然后,如果有人试图为不满足您要求的平台编译代码,他们可以自行修复它,也可以向您提交错误报告,具体取决于您对代码的处理方式. (2认同)

Eri*_*hil 6

此问题尚未准备好回答,因为问题尚未完全指定。特别是,没有说明应该如何精细地分布可以生成的值集。为了便于说明,请考虑生成 [0, 1] 的值,并考虑具有可表示值的浮点格式:

\n\n

0、1/16、2/16、3/16、4/16、6/16、8/16、12/16、1。

\n\n

这些值的几种分布可能被视为 \xe2\x80\x9cuniform\xe2\x80\x9d:

\n\n
    \n
  • 以相同的概率选择每个。这在离散值上是均匀的,但在值之间的实际距离上不具有均匀的密度。
  • \n
  • 选择每个的概率与其附近可表示值的密度成正比。
  • \n
  • 以相等的概率选择 0、4/16、8/16、12/16 和 1,以在间隔内保持相同的粒度。
  • \n
\n\n

我怀疑第一个是故意的,我会驳回它。第二个与 Steve Jessop 的建议类似,但仍不完全指定。是否应该以与从中点到下一个点的间隔成正比的概率选择 0?(这将给出 1/32 的概率。)或者它应该与以它为中心的区间(从 -1/32 到 1/32)相关联?(假设 1 也被分配了超出其自身 1/32 的区间,则该概率为 1/17。)

\n\n

您可能会认为这是一个闭区间,因此它应该停止在 0 和 1 处。但是假设对于某些应用程序,我们将 [0, 2] 上的分布切分为区间 [0, 1] 和 (1, 2].我们希望后两个区间的分布的并集等于前一个区间的分布。因此我们的分布应该很好地啮合。

\n\n

第三个案例也有类似的问题。也许,如果我们希望像这样保留粒度,应该以 1/8 的概率选择 0,以 1/4、1/2 和 3/4 三个点各以 1/4 的概率选择,以 1/8 的概率选择 1 。

\n\n

除了指定生成器所需属性的这些问题之外,提问者提出的代码还存在一些问题:

\n\n
    \n
  • 假设 RAND_MAX+1 是 2 的幂(因此除以它在二进制浮点算术中是 \xe2\x80\x9cnice\xe2\x80\x9d),除以 RAND_MAX 或 RAND_MAX+2 可能会导致计算结果出现一些不规律。生成的值。其中可能存在奇怪的量化。

  • \n
  • 当 1/(RAND_MAX+1) \xe2\x89\xa4 1/4 ULP(1) 时,RAND_MAX/(RAND_MAX+1) 将向上舍入并返回 1,因为间隔是 [0, 1)。(\xe2\x80\x9cULP(1)\xe2\x80\x9d 表示正在使用的浮点格式中值 1 的最小精度单位。)(在使用 long double 的测试中不会观察到这一点,其中 RAND_MAX适合有效数位,但它会发生,例如,当 RAND_MAX 为 2147483647 且浮点类型为 float,其有效数为 24 位时。)

  • \n
  • 乘法(b-a)和加法a会引入舍入误差,必须评估其后果。有许多情况,例如当b-a很小且a很大时、当ab跨越零时(因此导致 b 附近的粒度损失,即使可以表示更精细的结果)等等。

  • \n
  • (0, 1) 结果的下限是最接近 1/(RAND_MAX+2) 的浮点值。该界限与浮点值的精细度或所需的分布无关;它只是 rand 实现的一个产物。(0, 1/(RAND_MAX+2)) 中的值被省略,而没有任何源于问题规范的原因。类似的伪影可能存在于上端(取决于特定的浮点格式、rand 实现和间隔端点 b)。

  • \n
\n\n

我提交提问者对这个 \xe2\x80\x9csimple\xe2\x80\x9d 问题遇到不满意的答案的原因是它不是一个简单的问题。

\n

  • @Dr.PersonPersonII:正如史蒂夫·杰索普(Steve Jessop)指出的那样,你的问题陈述表明你厌倦了谷歌搜索这个,并且搜索结果包含很多无价值的材料。我的评论表明,这些观察结果是由于问题并不简单而引起的。 (4认同)