为什么这个随机值有25/75分布而不是50/50?

时间:2014-12-23 17:57:41

标签: java random double bit-manipulation probability

编辑:所以基本上我要写的是double的1位哈希。

我想将double映射到truefalse,并有50/50的机会。为此,我编写的代码选择了一些随机数(仅作为一个例子,我想在有规律性的数据上使用它并仍然得到50/50的结果),检查它们的最后一位并递增{{ 1}}如果它是1,或y如果它是0.

但是,此代码不断导致25%n和75%y。为什么不是50/50?为什么这么奇怪,但直截了当(1/3)分布?

n

示例输出:

public class DoubleToBoolean {
    @Test
    public void test() {

        int y = 0;
        int n = 0;
        Random r = new Random();
        for (int i = 0; i < 1000000; i++) {
            double randomValue = r.nextDouble();
            long lastBit = Double.doubleToLongBits(randomValue) & 1;
            if (lastBit == 1) {
                y++;
            } else {
                n++;
            }
        }
        System.out.println(y + " " + n);
    }
}

3 个答案:

答案 0 :(得分:164)

因为nextDouble的工作方式如下:(source

public double nextDouble()
{
    return (((long) next(26) << 27) + next(27)) / (double) (1L << 53);
}

next(x)使x个随机位。

现在为什么这很重要?因为第一部分(分割前)生成的数字大约有一半小于1L << 52,因此它们的有效数字并不完全填满它可以填充的53位,这意味着它的最低有效位。那些有效数字总是为零。


由于受到了很多关注,这里有一些额外的解释,说明Java(以及许多其他语言)中double的真实情况以及为什么它在这个问题中很重要。

基本上,double看起来像这样:(source

double layout

在这张照片中看不到的一个非常重要的细节是数字是&#34;标准化&#34; 1 使得53位分数以1开始(通过选择指数使得它是这样),然后省略1。这就是为什么图片显示分数(有效数字)的52位,但实际上有53位。

归一化意味着如果在nextDouble的代码中设置了第53位,那么该位是隐式前导1并且它消失,而其他52位被字面复制到结果的有效位数{ {1}}。但是,如果未设置该位,则必须向左移位剩余的位,直到它置位。

平均而言,生成的数字的一半落入有效数据向左移位的情况下(大约一半有0为最低有效位),另一半是移位至少1(或者只是完全为零),因此它们的最低有效位始终为0.

1:并非总是如此,很明显它不能用于零,​​没有最高1.这些数字称为非正规数或次正规数,见wikipedia:denormal number

答案 1 :(得分:48)

来自docs

  

方法nextDouble由Random类实现,如下所示:

public double nextDouble() {
  return (((long)next(26) << 27) + next(27))
      / (double)(1L << 53);
}

但它也说明了以下内容(强调我的):

  

[在Java的早期版本中,结果错误地计算为:

 return (((long)next(27) << 27) + next(27))
     / (double)(1L << 54);
     

这似乎是等效的,如果不是更好,但实际上它引入了一个很大的不均匀性,因为浮点数的舍入偏差:它是低阶位的三倍有效数字将是0而不是1 !这种不均匀性在实践中可能并不重要,但我们力求完美。]

至少从Java 5开始就有这个注释(Java&lt; = 1.4的文档落后于登录墙,懒得查看)。这很有趣,因为即使在Java 8中问题显然仍然存在。也许&#34;固定&#34;版本从未经过测试?

答案 2 :(得分:33)

考虑到如何表示浮点数,这个结果并不让我感到惊讶。让我们假设我们有一个非常短的浮点类型,只有4位精度。如果我们要生成0到1之间的随机数,均匀分布,则会有16个可能的值:

0.0000
0.0001
0.0010
0.0011
0.0100
...
0.1110
0.1111

如果他们看到机器的样子,你可以测试低阶位以获得50/50的分布。但是,IEEE浮点数表示为尾数的2倍;浮点中的一个字段是2的幂(加上固定的偏移量)。选择2的幂,以便&#34;尾数&#34; part始终是一个&gt; = 1.0且&lt; 2.0。这意味着,实际上,0.0000以外的数字将表示如下:

0.0001 = 2^(-4) x 1.000
0.0010 = 2^(-3) x 1.000
0.0011 = 2^(-3) x 1.100
0.0100 = 2^(-2) x 1.000
... 
0.0111 = 2^(-2) x 1.110
0.1000 = 2^(-1) x 1.000
0.1001 = 2^(-1) x 1.001
...
0.1110 = 2^(-1) x 1.110
0.1111 = 2^(-1) x 1.111

(二进制点之前的1是隐含值;对于32位和64位浮点数,实际上没有分配任何位来保存此1。)

但是看看上面的内容应该说明为什么,如果你将表示转换为位并查看低位,你将在75%的时间内得到零。这是由于所有小于0.5的值(二进制0.1000),这是可能值的一半,其尾数被移位,导致0出现在低位。当尾数具有52位(不包括隐含的1)作为double时,情况基本相同。

(实际上,正如@sneftel在评论中所建议的那样,我们可以在分发中包含超过16个可能的值,通过生成:

0.0001000 with probability 1/128
0.0001001 with probability 1/128
...
0.0001111 with probability 1/128
0.001000  with probability 1/64
0.001001  with probability 1/64
...
0.01111   with probability 1/32 
0.1000    with probability 1/16
0.1001    with probability 1/16
...
0.1110    with probability 1/16
0.1111    with probability 1/16

但我不确定这是大多数程序员所期望的那种分布,所以它可能不值得。此外,当值用于生成整数时,它不会获得太多收益,因为随机浮点值通常是。)