抢红包游戏策略分析

我先开始准备写个红包游戏,是提升自己并发能力的,当时的我的红包策略是,建立数组,然后分割红包,抽红包其实只是随机抽取一个准备好的红包而已。
但是看到知乎的一个回答, 给出的回答是每次的抢的动作都是重新计算金额,而不是提前准备数组。而给的代码,我开始是不相信的,代码如下(知乎复制要转载,直接贴我的代码(有优化)):

public static int getVlaue(int leftNum, int leftMoney) {
    if (leftNum == 1) {
        return leftMoney;
    }
    int value = 0;
    while (value == 0) {
        Random random = new Random();
        value = (int) Math.floor((leftMoney * 2 * random.nextDouble()) / (leftNum));
    }
    return value;
}

我开始是怀疑的,因为很明显,第一个人是不可能抽到特大红包的,比如抢一个10人10元红包,那第一名抽到的金额只能是0.01-1.99区间,我对此表示怀疑,于是,我进行了实验,此次试验,我要感谢我的女朋友,帮我记录每次的金额(虽然每次红包都是我发的)。
实验如下:每次发10个总和10元的随机红包,查看每次第一个人抽到的金额。结果如下:

0.1
0.14
1.02
1.33
1.51
1.45
0.1
0.71
1.22
0.1
1.28
1.98
1.71
0.98
0.74
0.84
1.35
0.28
1.28
1.98
1.22
0.55
1.41

其实只靠前几次的实验已经可以表明,微信抢红包的代码还真的是每次计算的,而且计算方式和知乎回答的类似,后面的实验主要是为了计算平均数,很容易求得1.012173913,如此接近1元。

回答中就说了,这么看,大家拿到红包值的期望是相同的,不同的就是方差,后抢的方差更大。这句话是准确的。
而我却先开始得出了一个错误的结论,后抢的拿到最大红包的概率最高,我开始还坚信不疑,还好自己写了个程序检验自己的想法。然而,实验数据给了我啪啪几个耳光。

代码如下:

public static void main(String[] args) {
    for (int k = 0; k < 10; k++) {

        int bagNum = 5;
        int redMoney = 10000;
        int[] maxNum = new int[bagNum];
        long[] redMoneyAll = new long[bagNum];
        int[] thisMoney = new int[bagNum];
        int[] lessNum = new int[bagNum];
        int[] moreNum = new int[bagNum];
        int lessMoney = redMoney / bagNum / 5;
        int moreMoney = redMoney / bagNum * 2;
        int testNum = 100000000;
        for (int i = 0; i < testNum; i++) {
            int maxMoney = 0;
            int left = redMoney;
            for (int j = 0; j < bagNum; j++) {
                int money = getVlaue(bagNum - j, left);
                left -= money;
                if (money > maxMoney) {
                    maxMoney = money;
                }
                redMoneyAll[j] += money;
                thisMoney[j] = money;
                if (money <= lessMoney) {
                    lessNum[j]++;
                }
                if (money >= moreMoney) {
                    moreNum[j]++;
                }
            }
            for (int j = 0; j < bagNum; j++) {
                if (thisMoney[j] == maxMoney) {
                    maxNum[j]++;
                }
            }
        }
        //抢到最大红包的次数
        System.out.println(Arrays.toString(maxNum));
        //抢到红包的总和
        System.out.println(Arrays.toString(redMoneyAll));
        //抢到低金额红包的次数
        System.out.println(Arrays.toString(lessNum));
        //抢到高金额红包的次数
        System.out.println(Arrays.toString(moreNum));
        System.out.println("-----------------");
    }
}

当抢红包的人数是20时结果:

[4576838, 4377864, 4206386, 4059613, 3933459, 3834654, 3767173, 3728459, 3720650, 3746752, 3826944, 3964041, 4164346, 4460892, 4896136, 5529302, 6482446, 7798889, 9656962, 9657081]
[50002718070, 50002627626, 50001032462, 50003111737, 50002708392, 50004737257, 50001317976, 50000571505, 50000403127, 49996491679, 49999902810, 50005244421, 49998822743, 49999951287, 49998065940, 49998419481, 49994571318, 49996669133, 49994639018, 49997994018]
[10012113, 10016531, 10030765, 10037281, 10047366, 10069771, 10083588, 10104987, 10135580, 10158172, 10190708, 10230917, 10293566, 10358022, 10458815, 10601780, 10828340, 11261068, 12371407, 12370918]
[0, 1270802, 1713302, 2116459, 2477255, 2820504, 3155265, 3491900, 3838015, 4188243, 4569811, 4981879, 5428237, 5931773, 6509665, 7201461, 8054925, 9208158, 10953681, 10955189]
-----------------
[4572949, 4378803, 4203046, 4052713, 3933936, 3835390, 3767160, 3726687, 3720473, 3749446, 3828768, 3961728, 4164835, 4463004, 4898745, 5529582, 6482281, 7802878, 9660490, 9656165]
[50000271967, 50000616411, 50000987631, 49998643266, 49998405318, 50000107898, 50003662270, 49998120262, 49999721470, 49999484894, 49999514493, 50001406235, 50001171184, 50001375281, 49996301426, 50000476160, 49994193768, 50001108688, 50002243958, 50002187420]
[10008361, 10018157, 10031418, 10040771, 10058984, 10070141, 10084583, 10107189, 10127649, 10160317, 10196262, 10231874, 10283413, 10358859, 10465230, 10594064, 10829929, 11256524, 12371327, 12373461]
[0, 1271647, 1712005, 2117840, 2476972, 2820586, 3154750, 3490036, 3835627, 4193975, 4570012, 4980619, 5428238, 5935976, 6512270, 7201188, 8062038, 9212021, 10960888, 10958000]
-----------------
[4571826, 4377540, 4201063, 4054847, 3933866, 3837181, 3768826, 3722158, 3720578, 3748920, 3823960, 3959583, 4165619, 4463934, 4895459, 5529584, 6485493, 7801952, 9663703, 9662186]
[50002118380, 49999702820, 50003022591, 49999778663, 50002924403, 50001322637, 50004054148, 49999171422, 49995803128, 49997878352, 49998246597, 49998869774, 49998286553, 49996822300, 50001349083, 49996001070, 49999705194, 50000107506, 50003455663, 50001379716]
[10007571, 10017424, 10030298, 10040333, 10054318, 10072289, 10079958, 10106667, 10129921, 10159588, 10192642, 10237198, 10290504, 10361079, 10455230, 10599565, 10832398, 11256638, 12371036, 12374109]
[0, 1272525, 1711548, 2117331, 2477783, 2817766, 3157038, 3491033, 3831968, 4192000, 4568601, 4979610, 5428099, 5933809, 6510789, 7200429, 8059698, 9207660, 10963594, 10959313]
-----------------
[4572981, 4376458, 4200883, 4055951, 3932480, 3835480, 3765593, 3725099, 3720134, 3750090, 3827692, 3958940, 4163808, 4461536, 4896323, 5534262, 6488890, 7802389, 9660812, 9659102]
[49995286926, 49995974562, 49997976039, 50000296348, 50002065839, 49997369551, 50001246883, 50006155828, 49997917822, 49998345891, 50002180877, 49999557460, 50000233116, 49998677245, 50000743578, 50003143633, 50001428448, 50001094832, 50000747559, 49999557563]
[10013355, 10017964, 10034150, 10043889, 10052661, 10069141, 10088215, 10101358, 10129608, 10157930, 10190898, 10232382, 10289694, 10362053, 10456853, 10598094, 10830191, 11259484, 12371256, 12370479]
[0, 1271805, 1711539, 2116830, 2477801, 2815936, 3153740, 3491475, 3835625, 4190403, 4570512, 4980273, 5427556, 5932133, 6511711, 7207827, 8063656, 9209408, 10960976, 10960139]
-----------------
[4573311, 4378003, 4202136, 4053264, 3933494, 3833101, 3763182, 3726083, 3722530, 3749763, 3824359, 3959948, 4162401, 4461756, 4898101, 5534713, 6487524, 7804439, 9660836, 9660175]
[50001077429, 50001125627, 49997587218, 50002976886, 49995269902, 49996527020, 49995015265, 49999919094, 50002877437, 49999378495, 50002968724, 49997769118, 49997393653, 49998748384, 49998110745, 50000476453, 50005505668, 49999966704, 50002975902, 50004330276]
[10011124, 10019662, 10030740, 10039404, 10058724, 10071074, 10082574, 10107339, 10130287, 10160711, 10189649, 10235756, 10288357, 10355661, 10454893, 10603569, 10824665, 11264680, 12369039, 12370212]
[0, 1272236, 1712019, 2116465, 2477668, 2818632, 3153028, 3489424, 3838319, 4193924, 4571361, 4981424, 5429101, 5932452, 6509413, 7203218, 8064012, 9214536, 10964377, 10962191]
-----------------
[4574287, 4374834, 4201407, 4055653, 3933783, 3835347, 3767130, 3728774, 3722644, 3753345, 3825835, 3959905, 4164325, 4462967, 4897715, 5533503, 6481555, 7797989, 9658968, 9658590]
[50000505137, 50001077683, 50004811641, 49995619774, 50002831710, 49996371902, 50001564849, 50002743963, 49997411219, 49997310800, 50000411780, 50005194159, 50008218607, 49994673525, 49998570681, 49997793854, 49997349670, 49999342529, 50000861094, 49997335423]
[10009230, 10018105, 10028033, 10046357, 10048388, 10071899, 10082736, 10104678, 10134297, 10159666, 10190613, 10232200, 10285166, 10360947, 10461795, 10606310, 10830961, 11259259, 12370265, 12371767]
[0, 1271066, 1709647, 2118199, 2474023, 2818984, 3155663, 3492532, 3837186, 4193024, 4571792, 4982795, 5433818, 5930830, 6511712, 7203743, 8058141, 9206730, 10956093, 10958977]
-----------------
[4577939, 4379043, 4206118, 4056708, 3930724, 3838551, 3766982, 3721994, 3719749, 3753686, 3827847, 3959269, 4162533, 4460430, 4898060, 5531019, 6483837, 7802174, 9655488, 9656699]
[49999633099, 50001193222, 50001694928, 50001779629, 49997045726, 50001149740, 50000014038, 49999852459, 50000628542, 50002231390, 49999637427, 50000815797, 50006613253, 49997332105, 49997856143, 50001295115, 49996047693, 50002440780, 49997615144, 49995123770]
[10010274, 10018618, 10030610, 10043782, 10056547, 10071578, 10088608, 10108731, 10131681, 10160241, 10192650, 10233747, 10286524, 10362396, 10454567, 10595785, 10831003, 11257365, 12369087, 12370105]
[0, 1271223, 1712952, 2117241, 2472786, 2819913, 3152235, 3490242, 3836394, 4194735, 4571401, 4980513, 5433711, 5928952, 6511181, 7199451, 8059882, 9211677, 10952078, 10957987]
-----------------
[4573201, 4372259, 4200967, 4055148, 3935519, 3835412, 3766176, 3729478, 3721431, 3750415, 3828287, 3960183, 4164123, 4462816, 4896499, 5535252, 6484469, 7802466, 9658551, 9656975]
[50002945390, 49998469294, 50004583616, 49994227494, 49998518284, 49992609143, 50000543031, 50000452265, 50002922823, 50000374215, 49999868585, 49999000098, 50004354853, 50001762123, 50002814235, 49999698048, 49999933128, 50002396711, 49995840897, 49998685767]
[10011592, 10017719, 10021033, 10044436, 10052958, 10074738, 10087469, 10107103, 10131405, 10157402, 10186276, 10236511, 10283426, 10354460, 10453672, 10603822, 10827279, 11259120, 12372686, 12371517]
[0, 1270096, 1711246, 2118171, 2477743, 2822558, 3154921, 3492253, 3835989, 4191516, 4573510, 4979246, 5429434, 5931921, 6511678, 7203538, 8058389, 9212299, 10957743, 10958772]
-----------------
[4568866, 4376679, 4205460, 4055532, 3935568, 3834653, 3762543, 3724595, 3719772, 3752969, 3828884, 3959032, 4162236, 4462787, 4900408, 5527128, 6482188, 7801825, 9664814, 9662436]
[50002251558, 49997684274, 49998941634, 50003590451, 50002002454, 49999226970, 49998700840, 50003011004, 49995718126, 50003766672, 50003758315, 49997432306, 49997184901, 49998038966, 49998482497, 49995239619, 49992467349, 50000861796, 50007794715, 50003845553]
[10012890, 10020371, 10027059, 10040660, 10055349, 10068721, 10082843, 10100699, 10136997, 10161361, 10188188, 10233841, 10286735, 10364574, 10455978, 10599252, 10826616, 11258112, 12367089, 12368813]
[0, 1270223, 1714049, 2116933, 2472923, 2817943, 3154581, 3492276, 3837664, 4194161, 4573734, 4976778, 5428270, 5930865, 6515008, 7199694, 8059255, 9211613, 10963409, 10960413]
-----------------
[4575267, 4380233, 4204981, 4053166, 3933135, 3836089, 3765854, 3729948, 3721917, 3750030, 3823565, 3958450, 4166052, 4461470, 4896485, 5533232, 6483171, 7802952, 9657979, 9655582]
[50008432979, 50001934715, 50002504981, 49999976928, 50000942022, 49996428820, 49996765316, 50000807343, 50003023706, 50000768882, 49996244621, 49994242910, 50001869577, 49999525564, 49998906956, 50003128761, 49995883057, 50001184941, 49997758679, 49999669242]
[10005138, 10018003, 10027874, 10036486, 10048555, 10070439, 10093163, 10112975, 10128587, 10161626, 10193072, 10235034, 10285765, 10352955, 10455983, 10593548, 10834363, 11259337, 12368241, 12368897]
[0, 1273750, 1710385, 2115812, 2475519, 2817896, 3152866, 3495652, 3834436, 4191671, 4569150, 4976490, 5432878, 5930809, 6508617, 7201979, 8058899, 9211850, 10956898, 10954118]

当抢红包人数降低为5人时:

[21752813, 20407968, 19575908, 19153394, 19142351]
[200015763264, 199999110760, 200014371031, 199999907411, 199970847534]
[9997739, 10219398, 10617461, 11672166, 11669745]
[0, 5374190, 7611906, 10306661, 10306389]
-----------------
[21755483, 20401380, 19572560, 19148798, 19154186]
[200012794444, 199998885189, 200008437488, 199979975866, 199999907013]
[10005644, 10216207, 10626195, 11676063, 11673124]
[0, 5371213, 7611673, 10307147, 10309843]
-----------------
[21753149, 20405771, 19572599, 19153496, 19147273]
[200020740081, 200006321844, 199992723690, 199995794714, 199984419671]
[9998447, 10215557, 10625845, 11670633, 11670336]
[0, 5368643, 7612694, 10308443, 10308834]
-----------------
[21748535, 20405241, 19567125, 19156118, 19155044]
[199999847495, 200007229053, 199983744367, 200021171970, 199988007115]
[10002117, 10211928, 10622513, 11665057, 11672105]
[0, 5368480, 7607574, 10309385, 10307901]
-----------------
[21750975, 20404565, 19571509, 19154291, 19150978]
[200010440455, 199997839160, 200000877904, 199987669816, 200003172665]
[10002749, 10220924, 10623676, 11679473, 11673440]
[0, 5372554, 7609414, 10307040, 10309572]
-----------------
[21751078, 20396630, 19571621, 19156238, 19156640]
[200011921131, 199991403174, 200000009652, 200010278316, 199986387727]
[10000361, 10221163, 10628932, 11672538, 11675377]
[0, 5366015, 7608768, 10312219, 10306294]
-----------------
[21753019, 20411879, 19569331, 19144988, 19153093]
[200007963276, 200014441725, 199997490484, 199976151349, 200003953166]
[10000434, 10218331, 10621400, 11674677, 11671919]
[0, 5370837, 7609203, 10301327, 10309860]
-----------------
[21754135, 20399798, 19572351, 19149898, 19155935]
[200006273803, 199991983517, 200000677332, 199987271642, 200013793706]
[10002653, 10222496, 10623215, 11676655, 11668236]
[0, 5370410, 7607804, 10306545, 10313036]
-----------------
[21744781, 20402079, 19575634, 19154849, 19155031]
[199993482769, 199997241580, 200004467826, 200001290221, 200003517604]
[10006781, 10215646, 10623065, 11675118, 11670558]
[0, 5372094, 7610540, 10306253, 10309426]
-----------------
[21748995, 20404165, 19564587, 19156626, 19157960]
[199984568968, 200012590462, 199974148286, 200006506837, 200022185447]
[10005745, 10220729, 10627366, 11674122, 11672402]
[0, 5375488, 7610525, 10313302, 10314589]

好吧,我又得出新的结论了。
抢到最大红包的概率,是先降低再增大。
之所以并不是我想的,越往后的抢到最大红包的概率越大,我是这样猜测的。
第一个人抢完红包后,对第二个人影响较大,比如,10元红包5人抢,第一名如果抢了接近4元,第二名的期望一下子就降到1.5元,而第一名抢到较大金额的概率还不低,但是越往后的人受到第一个人的影响越小,但是随着人数的累计,总的影响较大,所以摆动幅度特别大。
导致的结果就是:人数较少时的抢红包,先抢的人抢到最大红包的概率较大,人数较多时,越往后的人抢到最大红包的概率越大。而大家的期望是不变的。

今天又琢磨了一下这道题,发现当红包金额的两倍不能被总人数整除时,计算会有错误。

先举一个例子,红包还剩9分钱,还有4个人未抢,9*2*random/4的结果就是生成一个0-4.5的值
所以就会有如下结果
1/7的概率拿到4分
2/7的概率拿到3分
2/7的概率拿到2分
2/7的概率拿到1分
期望值是16/7,当然,这个结果和9/4(真实需要的期望)是很接近。但是16/7>9/4是事实。

问,当剩余金额*2不能被剩余人数平分时,是不是按之前算法的期望,一定会偏高吗?我们试一下
红包还剩m元,还剩n个人,m*2%n>0;
设:double d=m*2/n
设:int t = (int)d;
设:double x = d-x;
目标期望:m/n即(t+x)/2;
真实期望:x*t/(t+x-1)+(1+2+…+t-1)/(t+x-1)
目标期望-真实期望:(x*x-x)/(2*(t+x-1));
x是小数,所以x*x-x<0成立,所以真实期望偏高成立

因为差距很小,当红包金额很大时,很难发现问题,但是当红包设置为20分,人数设置为10人时,每个人抢到的红包总额为

[200286, 201676, 201891, 201778, 201570, 201111, 199999, 200364, 195958, 195367]
[199924, 201807, 202180, 202003, 201553, 201617, 200142, 200277, 195218, 195279]
[200142, 201888, 202309, 202101, 201132, 200855, 200167, 199892, 195832, 195682]
[199970, 201913, 201963, 201819, 201316, 201638, 200265, 200470, 195303, 195343]
[200143, 202084, 201865, 201754, 201642, 201490, 200241, 200344, 195326, 195111]
[200270, 201819, 202245, 202230, 201098, 200774, 200288, 199973, 195753, 195550]
[200125, 202229, 201986, 201771, 201553, 201062, 200312, 199749, 195550, 195663]
[199501, 202240, 202277, 202170, 201208, 201471, 200227, 199978, 195764, 195164]
[199963, 202233, 201957, 201842, 201579, 200691, 199984, 200141, 195886, 195724]

上面都是100000次测试的结果,很明显,第二个抢红包的,因为概率提高,导致抢到的红包金额总数更多,每次都高于平均值,最后两个人,因为前面的人都抢的偏高,他俩自然偏低。

那怎么解决这个问题,我进行了优化

public static int getVlaue(int leftNum, int leftMoney) {
    if (leftNum == 1) {
        return leftMoney;
    }
    Random random = new Random();
    int value = 1 + (int) Math.floor((getTrue((leftMoney * 2.0) / (leftNum)) - 1) * random.nextDouble());
    
    return value;
}

private static double getTrue(double k) {
    int t = (int) Math.floor(k);
    return (k - t) * (t - 1) / (2 * t - k) + t;
}

优化的思路很简单,我找到真正应该随机的值。寻找的计算方式如下:
目标期望:(t+x)/2
我以随机数t+y进行随机
得到得期望是yt/(t+y-1)+(1+2+…+t-1)/(t+y-1)
建立等式y
t/(t+y-1)+(1+2+…+t-1)/(t+y-1)=(t+x)/2(ps:入参k即为t+x)
得到y=(k - t) * (t - 1) / (2 * t - k),所以以t+y进行随机,才会试期望与目标期望一致

得到红包总数如下

[200017, 200344, 200081, 200013, 199913, 200115, 200191, 199497, 200457, 199372]
[199783, 200335, 199834, 200047, 199687, 199805, 199738, 200414, 200186, 200171]
[200242, 199726, 200335, 200260, 199622, 200016, 199844, 199979, 199949, 200027]
[200351, 199825, 199826, 199773, 200110, 199940, 199761, 199728, 200672, 200014]
[200211, 200411, 200035, 199465, 199471, 199974, 199745, 200776, 200039, 199873]
[199606, 200107, 199876, 199683, 200104, 199672, 200501, 200434, 199777, 200240]
[199772, 200113, 199987, 200255, 200344, 199721, 199773, 200048, 200138, 199849]
[200277, 199773, 199774, 199897, 199930, 200208, 200324, 200222, 199778, 199817]
[200312, 200009, 200256, 199998, 199852, 199974, 199911, 199950, 200005, 199733]
[199779, 199776, 199814, 200464, 200156, 199899, 200006, 199857, 200766, 199483]
[200117, 200493, 200025, 199750, 199919, 199275, 199703, 199835, 200529, 200354]

答案进行了200次上面的测试
每个人超过平均的次数为
[94, 99, 99, 112, 89, 86, 100, 95, 103, 115]
基本上可以认为,这个getTrue方法是生效的,做到让计算时的期望与目标期望相等

发布了127 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33321609/article/details/103970288
今日推荐