我想收集"最佳"方法,在一个地方生成所有四种类型间隔的随机数.我厌倦了谷歌搜索这个.搜索结果出现了很多垃圾.甚至相关的结果都是页面或博客,这些页面或博客通常都是错误的,或者讨论过自我指定的专家在某些技术性方面彼此不同意,通常他们的"答案"似乎暴露出他们不了解不同的类型(关闭)间隔开放,半开放.我厌倦了阅读关于在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.
如果你想要范围内的每一个双精度,概率与它和它的相邻双精度值之间的差值成比例,那么它实际上非常难.
考虑范围[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除了不确定性之外不会添加任何东西).
此问题尚未准备好回答,因为问题尚未完全指定。特别是,没有说明应该如何精细地分布可以生成的值集。为了便于说明,请考虑生成 [0, 1] 的值,并考虑具有可表示值的浮点格式:
\n\n0、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我怀疑第一个是故意的,我会驳回它。第二个与 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假设 RAND_MAX+1 是 2 的幂(因此除以它在二进制浮点算术中是 \xe2\x80\x9cnice\xe2\x80\x9d),除以 RAND_MAX 或 RAND_MAX+2 可能会导致计算结果出现一些不规律。生成的值。其中可能存在奇怪的量化。
当 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 位时。)
乘法(b-a)和加法a会引入舍入误差,必须评估其后果。有许多情况,例如当b-a很小且a很大时、当a和b跨越零时(因此导致 b 附近的粒度损失,即使可以表示更精细的结果)等等。
(0, 1) 结果的下限是最接近 1/(RAND_MAX+2) 的浮点值。该界限与浮点值的精细度或所需的分布无关;它只是 rand 实现的一个产物。(0, 1/(RAND_MAX+2)) 中的值被省略,而没有任何源于问题规范的原因。类似的伪影可能存在于上端(取决于特定的浮点格式、rand 实现和间隔端点 b)。
我提交提问者对这个 \xe2\x80\x9csimple\xe2\x80\x9d 问题遇到不满意的答案的原因是它不是一个简单的问题。
\n| 归档时间: |
|
| 查看次数: |
4164 次 |
| 最近记录: |