微信红包算法研究

背景:之前过年亲戚人都在抢红包,都想抢第一个,说是越早抢越大。其实这肯定是不正确的,微信在你发红包的时候应该已经生成所有的红包了,所以说你无论第几个抢,按说应该都一样,除非微信做了一些处理,比如说越早抢红包越可能手气最佳,过完年回来上班,突然想起了微信红包,就在想微信红包生成的策略。

20块钱10个红包,人均2块钱。因为理论上红包的金额应该在2块钱左右,而且越大概率越小,感觉这个很像正态分布函数,所以感觉微信红包属于截尾正态分布。手气最佳一般在2倍-3倍左右。
类似于这种(网上找的图):

所有红包在发送者发红包的时候都已经生成好了,领红包只是领生成的红包而已,点击别人发的红包,只会计算你点击的那一时刻还有没有红包,如果没有了,你就只能查看details了,所以说打开的时候出现的那个拆字相当于一个令牌,你可以凭借这个令牌去领红包,当然这个令牌在领红包的时候也有可能红包被领完了,所以你也就回去看details了,如果领到了就会告诉你领了多少钱。

每一次从红包金额池里随机出的金额应该是当时剩余总人数的均值,比如第一个人抽应该基于2的正态分布,如果他抽了4块钱,那么随机第二个红包的时候自然是基于16/9的正态分布,以此往后推,抽到倒数第一个就不用抽了,直接把剩下的都分了就行。而且因为红包最小是0.01嘛,所以每一次抽奖的时候都得留够足够的钱来用于发放剩下的红包,随意每一次随机都得判断。而且我做了一个限额倍数,因为随机出来的数字很可能在均值偏下的地方,多个累积起来,很可能就会造成最后一个红包过大的可能,所以我做了调整,当最后一个红包大于一定的倍数的时候,需要重新抽。另外,按这样抽奖,第一个人的均值应该最大,所以我在最后对生成的红包打乱了顺序。

随机生成红包的代码,做了一些简单的封装。
若Y~N(a,b^2) (b>0),则X=(Y-a)/b~(0,1)

package com.galaxy.fym.algorithm.maxsublist;

import java.math.BigDecimal;
import java.util.*;

/**
 * Created by fengyiming on 2017/2/17.
 *
 * @author fengyiming
 *         随机产生红包:金额正太分布
 *
 *         如果非标准正态分布X~N(μ,σ^2),那么关于X的一个一次函数 (X-μ)/σ ,就一定是服从标准正态分布N(0,1)。
 *         举个具体的例子,一个量X,是非标准正态分布,期望是10,方差是5^2(即X~N(10,5^2));那么对于X的线性函数Y=(X-10)/5,Y就是服从标准正态分布的Y~N(0,1)。
 */
public class RedPacket {

    private static Random random = new Random();

    private static BigDecimal MIN_VALUE = new BigDecimal("0.01");

    private static boolean isMin = false;

    /**
     * 生成红包
     *
     * @param amountValue 红包总金额
     * @param sizeValue   红包大小
     * @param maxMutValue 剩余红包限定倍数
     * @param sigmaValue  标准差倍数
     * @return
     */
    public static List<BigDecimal> getAllHotPacket(double amountValue, double sizeValue, double maxMutValue, double sigmaValue) {
        //红包总金额
        BigDecimal amount = new BigDecimal(String.valueOf(amountValue));
        BigDecimal restAmount = amount;
        BigDecimal size = new BigDecimal(String.valueOf(sizeValue));
        BigDecimal mu = restAmount.divide(size, 2, BigDecimal.ROUND_HALF_DOWN);
        BigDecimal avg = new BigDecimal(mu.toString());
        BigDecimal MAX_MUT = new BigDecimal(String.valueOf(maxMutValue));
        double sigma = sigmaValue <= 0 ? 1 : sigmaValue;
        List<BigDecimal> hotPacketPool;
        do {
            hotPacketPool = new ArrayList<BigDecimal>(size.intValue());
            int hotPacketSize = size.intValue() - 1;
            //随机出前size-1个红包,最后一个红包取剩余值,并且最后一个红包不能过大,有均值的限定倍数
            for (int i = 0; i < hotPacketSize; i++) {
                BigDecimal randomBigDecimal = getRandomHotPacketAmount(mu.doubleValue(), sigma, restAmount, size.intValue()-1);
                restAmount = restAmount.subtract(randomBigDecimal);
                //System.out.println("剩下的红包金额:" + restAmount);
                size = size.subtract(BigDecimal.ONE);
                mu = restAmount.divide(size, 2, BigDecimal.ROUND_HALF_DOWN);
                hotPacketPool.add(randomBigDecimal);
            }
            hotPacketPool.add(restAmount);
        } while (restAmount.compareTo(avg.multiply(MAX_MUT)) > 0);
        //打乱红包顺序,因为越早的红包均值最高
        //倒序遍历list,然后在当前位置随机一个比当前位置小的int数字,交换数字
        Collections.shuffle(hotPacketPool);
        return hotPacketPool;
    }

    /**
     * 根据剩余红包金额均值,标准差大小,计算出随机红包的大小
     *
     * @param mu
     * @param sigma
     * @param rest 剩下的钱
     * @param restSize 还剩多少红包
     * @return
     */
    private static BigDecimal getRandomHotPacketAmount(double mu, double sigma, BigDecimal rest, int restSize) {
        if(isMin){
            return MIN_VALUE;
        }
        BigDecimal radomNo;
        //剩余最小的钱
        BigDecimal minRest = MIN_VALUE.multiply(new BigDecimal(restSize));
        //随机出的红包也得满足剩余红包最少0.01
        do {
            radomNo = getRandom(mu, mu * sigma);
        }
        while (rest.subtract(radomNo).subtract(minRest).compareTo(BigDecimal.ZERO) < 0);
        if(rest.subtract(radomNo).subtract(minRest).compareTo(BigDecimal.ZERO) == 0){
            isMin = true;
        }
        BigDecimal randomBigDecimal = radomNo;
        //对红包金额取2位小数
        randomBigDecimal = randomBigDecimal.setScale(2, BigDecimal.ROUND_HALF_DOWN);
        //判断金额不能小于0.01元
        randomBigDecimal = randomBigDecimal.compareTo(MIN_VALUE) > 0 ? randomBigDecimal : MIN_VALUE;
        return randomBigDecimal;
    }

    /**
     * 产生mu sigma的正态分布的double值
     *
     * @param mu
     * @param sigma
     * @return
     */
    private static BigDecimal getRandom(double mu, double sigma) {
        double randomValue = random.nextGaussian() * sigma + mu;
        BigDecimal value = new BigDecimal(String.valueOf(randomValue)).abs();
        return value;
    }

    public static void main(String[] args) {
        BigDecimal all = BigDecimal.ZERO;
        List<BigDecimal> allHotPacket = getAllHotPacket(10d, 10d, 3d, 1d);
        int size = allHotPacket.size();
        BigDecimal max = BigDecimal.ZERO;
        int maxIndex = 0;
        for (int i = 0; i < size; i++) {
            BigDecimal amout = allHotPacket.get(i);
            System.out.println("第" + (i + 1) + "随机的红包金额大小:" + amout);
            if (amout.compareTo(max) > 0) {
                max = amout;
                maxIndex = i + 1;
            }
            all = all.add(amout);
        }
        System.out.println("所有红包金额为红包:" + all);
        System.out.println("手气最佳为:第" + maxIndex + "个红包,金额为:" + max);
    }

}
第1随机的红包金额大小:0.15
第2随机的红包金额大小:1.48
第3随机的红包金额大小:0.02
第4随机的红包金额大小:2.21
第5随机的红包金额大小:1.14
所有红包金额为红包:5.00
手气最佳为:第4个红包,金额为:2.21

第1随机的红包金额大小:0.13
第2随机的红包金额大小:0.65
第3随机的红包金额大小:2.30
第4随机的红包金额大小:0.95
第5随机的红包金额大小:0.97
所有红包金额为红包:5.00
手气最佳为:第3个红包,金额为:2.30

第1随机的红包金额大小:4.74
第2随机的红包金额大小:0.88
第3随机的红包金额大小:1.07
第4随机的红包金额大小:0.20
第5随机的红包金额大小:0.43
第6随机的红包金额大小:0.41
第7随机的红包金额大小:0.22
第8随机的红包金额大小:0.20
第9随机的红包金额大小:0.65
第10随机的红包金额大小:1.20
所有红包金额为红包:10.00
手气最佳为:第1个红包,金额为:4.74

第1随机的红包金额大小:0.63
第2随机的红包金额大小:0.33
第3随机的红包金额大小:1.35
第4随机的红包金额大小:1.00
第5随机的红包金额大小:0.70
第6随机的红包金额大小:3.19
第7随机的红包金额大小:0.19
第8随机的红包金额大小:1.50
第9随机的红包金额大小:0.18
第10随机的红包金额大小:0.93
所有红包金额为红包:10.00
手气最佳为:第6个红包,金额为:3.19

第1随机的红包金额大小:1.05
第2随机的红包金额大小:0.68
第3随机的红包金额大小:0.19
第4随机的红包金额大小:1.64
第5随机的红包金额大小:1.64
第6随机的红包金额大小:0.86
第7随机的红包金额大小:0.81
第8随机的红包金额大小:1.06
第9随机的红包金额大小:0.98
第10随机的红包金额大小:1.09
所有红包金额为红包:10.00
手气最佳为:第4个红包,金额为:1.64

测试随机数是否符合正态分布

package com.galaxy.fym.algorithm.maxsublist;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * Created by fengyiming on 2017/2/19.
 * 验证是否符合正态分布的算法
 */
public class Test2 {

    private static Random random = new Random();

    private final static double[] sigmas = {1d, 2d, 3d};

    private final static double DOUBLE_1 = 1d;

    private final static double DOUBLE_0 = 0d;

    public static void main(String[] args) {
        int size = 10000000;
        double mu = 2d;
        double sigmaValue = 1d;
        //标准差的切换
        double sigma = mu * sigmaValue;
        Map<Double, Double> map = installMap();
        for (int i = 0; i < size; i++) {
            double radomNo = getRandom(mu, sigma);
            if (radomNo > mu - sigma && radomNo <= mu + sigma) {
                map.put(sigmas[0], map.get(sigmas[0]) + DOUBLE_1);
            }
            if (radomNo > mu - 2  sigma && radomNo <= mu + 2  sigma) {
                map.put(sigmas[1], map.get(sigmas[1]) + DOUBLE_1);
            }
            if (radomNo > mu - 3  sigma && radomNo <= mu + 3  sigma) {
                map.put(sigmas[2], map.get(sigmas[2]) + DOUBLE_1);
            }
        }
        double mu1 = map.get(sigmas[0]);
        double mu2 = map.get(sigmas[1]);
        double mu3 = map.get(sigmas[2]);
        System.out.println("随机数出现在1个标注差内的概率为:" + mu1 / size);
        System.out.println("随机数出现在2个标注差内的概率为:" + mu2 / size);
        System.out.println("随机数出现在3个标注差内的概率为:" + mu3 / size);

    }

    private static double getRandom(double mu, double sigma) {
        return random.nextGaussian() * sigma + mu;
    }

    private static Map<Double, Double> installMap() {
        Map<Double, Double> map = new HashMap();
        map.put(sigmas[0], DOUBLE_0);
        map.put(sigmas[1], DOUBLE_0);
        map.put(sigmas[2], DOUBLE_0);
        return map;
    }
}

当然,微信红包肯定不是这么简单,也有可能不是基于正态分布,所以为了增加他的可玩性,应该做了很多的处理,比如说在红包均值很高的时候总有一些人会出现好几个几分的,这个估计应该是做了处理的。

时间: 2024-08-03 20:16:49

微信红包算法研究的相关文章

基于PHP微信红包的算法探讨_php实例

突发奇想给校友微信群发了红包,我设定红包总额为10元,支持28个人随机领取. 于是一个有趣的结果出现了:A 领取了 0.26元 B 领取了 0.29元 C 领取了 0.02元 D 领取了 0.56元 E 领取了 0.64元 --微信是采用什么样的算法做到的?简单百度了下,目前尚未有官方的说明,仅仅在知乎里有一个较为热门的讨论,链接戳这里,不过他们讨论的太过于深入,有掉坑之嫌. 我按照自己的逻辑尝试了下,这个算法需要满足以下几点要求: 1.每个人都要能够领取到红包: 2.每个人领取到的红包金额总和

微信红包随机生成算法php版_php实例

想了想,自己写写php版的微信红包随机生成算法,能不能实现类似的功能(其实也不敢说是算法).// $bonus_total 红包总金额// $bonus_count 红包个数// $bonus_type 红包类型 1=拼手气红包 0=普通红包 function randBonus($bonus_total=0, $bonus_count=3, $bonus_type=1){ $bonus_items = array(); // 将要瓜分的结果 $bonus_balance = $bonus_to

java 微信随机红包算法代码实例_java

前几天的一个晚上突然想到微信红包应该有一个随机算法,就自己试着写了下,也不知道对不对,看了看网上的说法,好像到现在为止官方也没有给出一个确切的算法,只好在这里献丑了,先贴出代码: public static double [] getMoney(double money, int num){ Random r = new Random(); DecimalFormat format = new DecimalFormat(".##"); double middle = Double.p

php实现的微信红包算法分析(非官方)_php技巧

本文实例讲述了php实现的微信红包算法.分享给大家供大家参考.具体如下: 最近一直在微信群里体验红包功能,红包类型有两种: 1. 普通红包 2. 拼手气红包 普通红包就不用多解析了,大锅饭原理,平分. 拼手气红包讲的是手气(运气),有人可以抢到很多,有人抢的少得可怜,当然也不是先抢就一定多,说到底了就是随机. 想了想,自己写写看,能不能实现类似的功能(不敢说是算法). // $bonus_total 红包总金额 // $bonus_count 红包个数 // $bonus_type 红包类型 1

大家在抢红包,程序员在研究红包算法_php实例

除夕全天微信用户红包总发送量达到10.1亿次,摇一摇互动量达到110亿次,红包峰值发送量为8.1亿次/分钟. 抛开微信红包的市场价值不谈,红包本身的算法也引发了热议,由于官方没有给出明确的说法,各家也是众说纷纭,小编下面也为大家带来几种分析. 首先看看数据分析帝 大多数人都做出自己的猜测,这也是在不知道内部随机算法的时候的唯一选择,但是大多数人没有给出自己亲自的调查结果.这里给出一份100样本的调查抽样样本数据,并提出自己的猜测. 1. 钱包钱数满足截尾正态随机数分布.大致为在截尾正态分布中取随

代码-把微信红包拼手气和普通红包结合成一个红包使用

问题描述 把微信红包拼手气和普通红包结合成一个红包使用 50C 如何实现把微信红包拼手气和普通红包结合成一个红包使用,目前还没有人可以实现其中的原理,欢迎前来探讨 解决方案 你的问题好奇怪,不过我还是发表一下我自己的看法.首先手气红包是用随机算法跑出来的,而一般红包就是一个可以由用户自己来设定大小的.然后针对你这个问题,想把两者合起来.只需要把两者的处理程序合在一起就行了.

微信红包如何抢大包、直播反垃圾、老骗局翻新……这一期宅客周刊你值得拥有

     1.直播狂欢背后,如何揪出"不按套路出牌"的主播和观众? 在你对着美丽的女主播流口水时,偶尔会看到屏幕上冒出一条不和谐的弹幕,例如:"看艾薇:加XXXXX." 对于这种不健康的信息,你可能会感到非常愤怒,然后按照号码加一下... 这样的观众"福利",对于直播平台 CEO 来说却是不折不扣的灾难.每一个这样的弹幕,都把他向"快播王欣"的角色推进了一步.更何况时不时出现的"造人"直播,恨不能导致&qu

微信红包推出后,腾讯的得失

自从微信5.2版本发布,很多人的微信瞬间被"红包"刷屏. 微信红包的背后其实是一个名为"新年红包"的公众账号,它由腾讯财付通推出.可以预计的是,随着大量年轻人回乡与亲人团聚,春节期间微信红包势必会更大范围的病毒式传播,腾讯在不用花费任何推广费用的情况下就引爆了马年第一个全民话题,这个四两拨千斤的产品迅速被引爆的背后究竟隐藏了腾讯的那些得失? 微信红包为何这么红? 首先它很方便.用户只需进入"新年红包"公众号,选择发几个红包.发放的金额,写好祝福语

抢到0.01也许不是点背,北大发现微信红包不是随机分配

       你可能遇到过这样的情况,老板把你和小张拖到一个群发红包慰劳一下,这个微信红包价值200元,你眼疾手快,先点开,一看:摔!0.01元!小张乐呵呵地领了剩下的红包. 虽然200元丢在路上你都不一定心动(只是假设下,正常人可能还是会捡起来),但是,这200元好歹也是老板的心意,抢到0.01元是不是略微有点沮丧? 真不是你运气差.最近,北京大学师生通过研究发现,抢微信红包的金额可能并非随机分配,而与用户注册时间有一定关联. 这一研究结果由北京大学元培学院本科生李星宇在微信公众号"思考者iT