浅谈前端随机抽样算法以及验证方法

在项目中要在前端做一个抽样的功能。

前端某个方法执行100次,抽取10次,进行数据上报:比如某个按钮,点击要记录日志,但是犹豫这个按钮触发的可能性比较大,我们希望用户点击100次才上报10次,也就是抽取10分之1进行上报。

其实这种场景下,在前端做抽样是比较难的。我们要考虑到一个问题,如果用户点击不到10次如何处是上报还是不上报?

开始,想到用随机数的方法来写一个命中率的函数,不管用户点击了1次还是10次,每次都有一个命中率,如果这个命中率是10分之一,那么对于全部用户来算的话,是不是就是10分之一了呢。理论上好像行得通。然后我写了个方法:

 
 const hittingRatio = function(floatNum){
    let randomNum = Math.random().toFixed(1)
    let ratio = parseFloat(floatNum)
    randomNum = parseFloat(randomNum)
    return randomNum <= ratio
}

 

怎么验证这个方法到底是否可靠呢?于是我在控制台测试这个脚本,但执行次数足够大的时候,是不是满足要求。比如我执行100次,理论上,返回true的次数应该有10左右?9次或者11次也是可能的。

于是写个测试代码来测试:

var n = 0
var hittingRatio = function(floatNum){
    let randomNum = Math.random().toFixed(1)
    let ratio = parseFloat(floatNum)
    randomNum = parseFloat(randomNum)
    // console.log(randomNum, ratio)
    if(randomNum <= ratio){
        n+=1
    }
    // return randomNum <= ratio
}
for(let i =0;i<100;i++){
    hittingRatio(0.1)
}
console.log(n)

我在控制台执行上面这段脚本,执行10次,把结果进行了统计如下:

次数 每100次命中数
1 12
2 16
3 17
4 10
5 16
6 15
7 19
8 10
9 11
10 6
平均数 13.2

xx我期望的是这个平均数应该是接近10,但是时间上这么算下来误差实在有点大。试试把次数调整多一点:

把100次调整为1000次后的效果:

次数 每100次命中数 1000次命中
1 12 153
2 16 151
3 17 144
4 10 155
5 16 145
6 15 162
7 19 146
8 10 152
9 11 142
10 6 156
平均数 13.2 150.6
命中比例 13.20% 15.06%

简直要崩溃,执行次数越多,居然越来越偏离我的期望。看来这个方法算法有问题。

然后又重新写了一个方法,也是利用随机函数来计算。期望但这个函数执行足够多的次数时,命中率为无线接近我们的抽样比例。比如我传参是0.1,10000次,返回true的次数应该无线接近1000;这样才对。

然后,跟同时沟通过,发现搞复杂了,其实很简单,我想要的其实就是 Math.random() < percent 。。。。。:  

var s = 0
function Hitting(percent){
    if (typeof percent === ‘string’){
        percent = parseFloat(percent)
    }
    return Math.random() < percent
};
 for(var i = 0;i<100;i++){
    Hitting(.1)
} 
console.log(s);

同样统计下:我们发现,这个方法,但次数越多时,越来越解决10%了!

次数 每100次命中数 1000次命中 10000次命中 新方法100次命中 新方法1000次命中 新方法10000次命中
1 12 153 1500 13 106 939
2 16 151 1454 12 122 945
3 17 144 1484 10 86 1021
4 10 155 1448 12 111 1005
5 16 145 1512 13 101 979
6 15 162 1552 18 93 1013
7 19 146 1503 5 114 984
8 10 152 1440 11 89 1031
9 11 142 1457 10 96 998
10 6 156 1561 10 93 1009
平均数 13.2 150.6 1491.1 11.4 101.1 992.4
命中比例 13.20% 15.06% 14.90% 11.40% 10.11% 9.92%

 

这么对比下看起来,后面的方法更接近抽样比例0.1。

然后再试试 ,把参数调整到0.01;测试代码如下

for(var k =0;k<10;k++){

	var s = 0
    function Hitting(percent){
        if (typeof percent === 'string'){
        percent = parseFloat(percent)
    }
    return Math.random() < percent
    };
    for(var i = 0;i<100;i++){
        Hitting(.01)
    } 
    console.log(s);
}

结果如下:期望值为0.01 = 1%,允许误差。所以,样本数越大,越接近这个比例。

次数 100次命中 1000次命中 100000命中
1 2 8 982
2 1 18 999
3 0 8 1020
4 0 5 1018
5 3 11 983
6 0 13 968
7 1 9 1006
8 3 5 982
9 2 9 1001
10 0 13 958
平均数 1.2 9.9 991.7
命中比例 1.20% 0.99% 0.99%

 

所以,最后结论就是后面这个方法可行。

// percent 为浮点数,支持0.01-0.99
function Hitting(percent){
    if (typeof percent === 'string'){
        percent = parseFloat(percent)
    }
    return Math.random() < percent
};

发表评论

记录工作生活点滴。

返回
顶部