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

我想收集在一个地方生成所有四种类型间隔的随机数的“最佳”方法。 我厌倦了谷歌搜索这个。 搜索结果出现了很多废话。 甚至相关的结果都是页面或博客,这些页面或博客通常都是错误的,或者讨论过自我指定的专家在一些技术性方面彼此不同意,通常他们的“答案”似乎暴露出他们不了解不同的类型(关闭)间隔开放,半开放。 我厌倦了阅读关于在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)*(ba) + a; /* x2 will be an element of [a,b) */ x2=((long double)rand()/((long double)RAND_MAX+1))*(ba) + a; /* x3 will be an element of (a,b] */ x3=(((long double)rand()+1)/((long double)RAND_MAX+1))*(ba) + a; /* x4 will be an element of (a,b) */ x4=(((long double)rand()+1)/((long double)RAND_MAX+2))*(ba) + a; 

对于单位间隔(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)); 

我相信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] 。 在该范围的非常小的第一部分中存在绝对桶值:其中一百万个在01000000*DBL_MIN之间1000000*DBL_MIN ,并且DBL_MIN大约是2 * 10 -308 。 整个范围内有超过2^32值,所以很明显一次调用rand()并不足以生成所有值。 您需要做的是均匀地生成双精度的尾数,并选择具有指数分布的指数,然后稍微捏一下以确保结果在范围内。

如果你不需要范围内的每一个双精度,则开启和闭合范围之间的差异是相当不相关的,因为在“真实的”连续均匀随机分布中, 任何精确值发生的概率无论如何都是0。 所以你不妨在开放范围内生成一个数字。

所有这一切:是的,你提出的实现产生的值在你说的范围内,对于闭合和半闭的范围,它们产生概率为1/(RAND_MAX+1)左右的端点。 这对于许多或大多数实际目的来说已经足够了。

如果RAND_MAX+2double精度可以精确表示的范围内,则可以使用+1和+2工作。 这对于IEEE双精度和32位int是正确的,但它实际上并不受C标准的保证。

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

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

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

关于这些值的几个分布可能被认为是“统一的”:

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

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

你可能会认为这是一个封闭的区间,所以它应该在0和1处停止。但是假设我们在某些应用程序中将[0,2]上的分布切换到区间[0,1]和(1,我们希望后两个区间的分布联合等于前一区间的分布。所以我们的分布应该很好地融合。

第三种情况也有类似的问题。 也许,如果我们希望保留像这样的粒度,则应选择0,概率为1/8,三个点为1 / 4,1 / 2和3/4,概率为1/4,1,概率为1/8 。

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

  • 假设RAND_MAX + 1是2的幂(并因此除以它在二进制浮点算术中是“好的”),除以RAND_MAX或RAND_MAX + 2可能导致所生成的值中的一些不规则。 它们中可能存在奇数量化。

  • 当1 /(RAND_MAX + 1)≤1/ 4 ULP(1)时,RAND_MAX /(RAND_MAX + 1)将向上舍入并在不应该返回1时因为间隔为[0,1]。 (“ULP(1)”表示正在使用的浮点格式中值1的最小精度单位。)(在具有long double的测试中未观察到这种情况,其中RAND_MAX适合有效数字的位,但是例如,它将发生在RAND_MAX为2147483647且浮点类型为float且具有24位有效位数的情况下。)

  • 乘以(ba)并添加引入的舍入误差,必须评估其后果。 存在许多情况,例如当ba小且a大时, ab跨越零(因此即使可以表示更精细的结果,也导致b附近的粒度损失),等等。

  • (0,1)的结果的下限是最接近1 /(RAND_MAX + 2)的浮点值。 该界限与浮点值的精细度或期望的分布无关; 它只是rand实现的工件。 省略(0,1 /(RAND_MAX + 2))中的值,而不会产生问题规范的任何原因。 上端可能存在类似的工件(取决于特定的浮点格式,rand实现和间隔端点,b)。

我提交了提问者遇到不满意答案的原因,这个“简单”的问题是它不是一个简单的问题。

首先,在[a,b]上生成随机数。 要在[a,b)上生成随机数,只需在[a,b]上生成一个随机数,检查它是否等于b,如果是,则再试一次。 类似地,对于所有其他开放区间变体。

在我的脑海中,我只提供不同浮点和整数类型的所有变体(模板化C ++实现的奖励点)并且我用更好的东西替换rand() drand48()想到drand48()

以下是我用于查找正在生成的数字中的基本错误的(非常粗略的)测试。 它并不是要表明生成的数字是好的,但它们并不坏。

 #include #include #include int main(int argc, char *argv[]) { long double x1,x2,x3,x4; if ( argc!=2 ) { printf("USAGE: %s [1,2,3,4]\n",argv[0]); exit(EXIT_SUCCESS); } srand((unsigned int)time(NULL)); printf("This program simply generates random numbers in the chosen interval\n" "and looks for values on the boundary or outside it. When an\n" "allowable boundary is found, it reports it. Unexpected \"impossible\"\n" "values will be reported and the program will terminte. Under\n" "normal circumstances, the program should not terminate. Use ctrl-c.\n\n"); switch ( atoi(argv[1]) ) { case 1: /* x1 will be an element of [0,1] */ printf("NOTE: Testing [0,1].\n"); while ( 1 ) { x1=((long double)rand()/RAND_MAX); if ( x1==0 ) { printf("x1=0 ENCOUNTERED.\n"); } else if ( x1==1 ) { printf("x1=1 ENCOUNTERED.\n"); } else if ( x1 < 0 ) { printf("x1<0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x1 > 1 ) { printf("x1>0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } } break; case 2: /* x2 will be an element of [0,1) */ printf("NOTE: Testing [0,1).\n"); while ( 1 ) { x2=((long double)rand()/((long double)RAND_MAX+1)); if ( x2==0 ) { printf("x2=0 ENCOUNTERED.\n"); } else if ( x2==1 ) { printf("x2=1 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x2 < 0 ) { printf("x2<0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x2 > 1 ) { printf("x2>0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } } break; case 3: /* x3 will be an element of (0,1] */ printf("NOTE: Testing (0,1].\n"); while ( 1 ) { x3=(((long double)rand()+1)/((long double)RAND_MAX+1)); if ( x3==1 ) { printf("x3=1 ENCOUNTERED.\n"); } else if ( x3==0 ) { printf("x3=0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x3 < 0 ) { printf("x3<0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x3 > 1 ) { printf("x3>0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } } break; case 4: /* x4 will be an element of (0,1) */ printf("NOTE: Testing (0,1).\n"); while ( 1 ) { x4=(((long double)rand()+1)/((long double)RAND_MAX+2)); if ( x4==0 ) { printf("x4=0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x4==1 ) { printf("x4=1 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x4 < 0 ) { printf("x4<0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } else if ( x4 > 1 ) { printf("x4>0 ENCOUNTERED. Abnormal termination.\n"); exit(EXIT_FAILURE); } } break; default: printf("ERROR: invalid argument. Enter 1, 2, 3, or 4 for [0,1], [0,1), (0,1], and (0,1), respectively.\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }