超几何模拟,通过一次洗牌一次性选择会产生错误的结果

时间:2017-09-28 08:49:27

标签: javascript arrays algorithm simulation probability-distribution

我模拟了那里有N个大理石的模型,其中K弹珠很好。我们从N个大理石中挑选出n个大理石,并且被要求提取n个被挑选的那个中的k个是好的概率。

我做了两个方法:在两个方面我都生成了一个包含K' true'值和N-K' false'值。但是在第一种方法中,我改组了这个数组并选择了n个第一个值并计算了其中有多少是真的'。在第二种方法中,我随机选择了一个索引并从数组中删除了该元素,循环了n次(当然还算了我得到的' true'元素)。

结果分布应为HyperGeometric(N, K, n)。第一种方法给了我错误的结果,而第二种方法给出了正确的结果。为什么选择洗牌数组的n个第一个元素或者我做错了什么呢?这是我的Javascript代码:

function pickGoodsTest(N, K, n) {
    var origArr = generateArr(N, i=> i<K);
    shuffle(origArr);
    var goods = 0;
    for (let i=0; i<n; i++) if(origArr[i]) goods++;
    return goods;
}

function pickGoodsTest2(N, K, n) {
    var origArr = generateArr(N, i=> i<K);
    var goods = 0;
    for (let i=0; i<n; i++) {
        let rndInd = randInt(0, origArr.length-1);
        let wasGood = origArr.splice(rndInd, 1)[0];
        if (wasGood) goods++;
    }
    return goods;
}

//helper functions:

function generateArr(len, indFunc) {
    var ret = [];
    for (let i=0; i<len; i++) {
        ret.push(indFunc(i));
    }
    return ret;
}

function randInt(a, b){return a+Math.floor( Math.random()*(b-a+1) );}

function shuffle(arr) {
    let arrLen = arr.length;
    for (let i=0; i<arrLen; i++) {
        let temp = arr[i];
        let rndInd = randInt(0, arrLen-1);
        arr[i] = arr[rndInd];
        arr[rndInd] = temp;
    }
}

这些是结果的图表,其中值N = 10,K = 6,n = 5(模拟500000次):

enter image description here

黄点是超几何pmf的值。

2 个答案:

答案 0 :(得分:3)

你改变阵列的方式有偏见,我建议改用Fisher-Yates shuffle:

function shuffle(arr) {
    let arrLen = arr.length;
    for (let i=0; i<arrLen; i++) {
        let temp = arr[i];
        let rndInd = randInt(0, i);
        arr[i] = arr[rndInd];
        arr[rndInd] = temp;
    }
}

答案 1 :(得分:3)

下面的代码证明您的shuffle机制是错误的。代码在随机的所有可能结果中混合大小为3的数组,并收集数字在特定位置的机会统计数据。

import java.util.Arrays;

public class TestShuffle {
    public static void main(String[] args) {
        int[][] stat = new int[3][3];

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 3; k++) {
                    int[] y = {0, 1, 2};
                    swap(y, 0, i);
                    swap(y, 1, j);
                    swap(y, 2, k);

                    stat[0][y[0]]++;
                    stat[1][y[1]]++;
                    stat[2][y[2]]++;
                }
            }
        }

        System.out.println(Arrays.deepToString(stat));
    }

    private static void swap(int[] y, int i, int k) {
        int tmp = y[i];
        y[i] = y[k];
        y[k] = tmp;
    }
}

输出

[[9, 10, 8], [9, 8, 10], [9, 9, 9]]

这意味着数字“1”在0位置的机会大于1/3。现在是10/27。