老黄历了,几年前就有人拿出来拉了一波流量的问题,今天看到又被人翻出来,再圈一波流量,群里吵翻天,绕得不行。题目大意如下:

有4只小鸭子,随机分布于一个圆形当中。这就是全部的信息

问:这4只小鸭子同时处在同一个半圆中的概率是多少

本着一名码农的简单务实精神,我们先直接撸一份代码试试看

因为小鸭子距离圆心的距离并不会影响到这只小鸭子能否被划分到某个半圆里,所以这里对模型简化下,把小鸭子的位置简化为从圆心到圆边的投射点,那么这个问题就间接地简化为

在一个圆形的边上随机选定4个点,这4个点恰好在某个180°的弧度范围内的概率

又圆的总共角度为360°,且是一个首尾相接的情况,那么在处理数据的时候,我们采用和处理循环链表、数组相似的处理方法,复制双倍长度的方法。即,假设生成了一个在65°上的点,那么我们同步的添加一个(65+360)°的点的数据,这样可以极大地方便计算,代码如下

public class DuckTest {
    private static final Random RANDOM = new Random();
    private static final int[] CNT = new int[2];

    @Test
    public void main() {
        int loop = 2000000000;
        for (int i = 0; i < loop; i++) {
            randomDuck();
        }
        System.out.println("CNT = " + Arrays.toString(CNT));
        System.out.println((float) CNT[1]/loop);
    }

    private void randomDuck(){
        int[] arr = new int[8];
        for (int i = 0; i < 4; i++) {
            int pos = RANDOM.nextInt(360);
            arr[i] = pos;
            arr[i+4] = pos+360;
        }
        Arrays.sort(arr);
        if (isInSemicircle(arr)){
            CNT[1]++;
        }else{
            CNT[0]++;
        }
    }


    private boolean isInSemicircle(int[] arr) {
        for (int i = 0; i < 4; i++) {
            if (arr[i+3] - arr[i] <= 180){
                return true;
            }
        }
        return false;
    }
}

直接揭晓答案,概率是50%

数据不至于有错,不过我这还是有一点点问题,一直都是50.4%的概率,这个`.4`应该是边界值判定的一点误差问题。

所以这个50%的结果怎么理解呢?

首先一个误区,不是先划定半圆,然后算鸭子在这个半圆中的概率,而是4只鸭子先在圆形湖里,然后判断这4个鸭子能否用一个半圆框住的概率。

有了这样的认知之后我们再来看实际情况,在湖里的4只鸭子中,任选一只鸭子,必然可以划出一个半圆将这只鸭子包含在其中。下一步,尝试判断任意另外一只鸭子是否在这个半圆中,这显然依旧是100%的概率,因为即使这两只鸭子是在同一直径划线的两侧,也就是弧度相差为π,角度为180°的最大的情况下,我们依旧可以将这两只鸭子划入一个半圆中

当湖里有4只鸭子的时候,其中必定有3只鸭子在同一侧的半圆中,这里是指必定能找到3只鸭子在同一侧,而不是任意取3只鸭子会在同一侧

此时概率依旧为100%,那么接下来要求的情况,即是剩余两只鸭子在目前划出的这个直径的划线的同一侧的概率,即

1/2 * 1/2  + 1/2 * 1/2 = 1/2

两只鸭子同时在某一侧的概率和同时在另一侧的概率之和