[!] 讨论: 随机红包算法问题 - V2EX
zhuzhibin

[!] 讨论: 随机红包算法问题

  •  
  •   zhuzhibin Sep 28, 2019 3641 views
    This topic created in 2432 days ago, the information mentioned may be changed or developed.

    我相信大家都有自己的看法,所以我也是抱着学习的心态问这个问题

    举个例子了呗:

    10 人群聊咯,我发了 100 元红包,然后 10 人随机瓜分 100 元红包

    方案应该很多,例如随机比例等等 ,嘿嘿 大家畅所欲言哈,想请教下各位

    17 replies    2019-09-28 20:25:39 +08:00
    niknik
        1
    niknik  
       Sep 28, 2019
    一条绳子,九个结,每段长度,然后十段绳子随机分出去(自我猜测应该是这样的,嘻嘻)
    Baymaxbowen
        2
    Baymaxbowen  
       Sep 28, 2019
    memedahui
        3
    memedahui  
       Sep 28, 2019   3
    import java.util.LinkedList;
    import java.util.List;

    /**
    * Function: 模拟微信红包生成,以分为单位
    *
    * @author crossoverJie
    * Date: 03/01/2018 16:52
    * @since JDK 1.8
    */
    public class RedPacket {

    /**
    * 生成红包最小值 1 分
    */
    private static final int MIN_MOnEY= 1;

    /**
    * 生成红包最大值 200 人民币
    */
    private static final int MAX_MOnEY= 200 * 100;

    /**
    * 小于最小值
    */
    private static final int LESS = -1;
    /**
    * 大于最大值
    */
    private static final int MORE = -2;

    /**
    * 正常值
    */
    private static final int OK = 1;

    /**
    * 最大的红包是平均值的 TIMES 倍,防止某一次分配红包较大
    */
    private static final double TIMES = 2.1F;

    private int recursiveCount = 0;

    public List<Integer> splitRedPacket(int money, int count) {
    List<Integer> mOneys= new LinkedList<>();

    //金额检查,如果最大红包 * 个数 < 总金额;则需要调大最小红包 MAX_MONEY
    if (MAX_MONEY * count <= money) {
    System.err.println("请调大最小红包金额 MAX_MOnEY=[" + MAX_MONEY + "]");
    return moneys ;
    }


    //计算出最大红包
    int max = (int) ((money / count) * TIMES);
    max = max > MAX_MONEY ? MAX_MONEY : max;

    for (int i = 0; i < count; i++) {
    //随机获取红包
    int redPacket = randomRedPacket(money, MIN_MONEY, max, count - i);
    moneys.add(redPacket);
    //总金额每次减少
    money -= redPacket;
    }

    return moneys;
    }

    private int randomRedPacket(int totalMoney, int minMoney, int maxMoney, int count) {
    //只有一个红包直接返回
    if (count == 1) {
    return totalMoney;
    }

    if (minMOney== maxMoney) {
    return minMoney;
    }

    //如果最大金额大于了剩余金额 则用剩余金额 因为这个 money 每分配一次都会减小
    maxMOney= maxMoney > totalMoney ? totalMoney : maxMoney;

    //在 minMoney 到 maxMoney 生成一个随机红包
    int redPacket = (int) (Math.random() * (maxMoney - minMoney) + minMoney);

    int lastMOney= totalMoney - redPacket;

    int status = checkMoney(lastMoney, count - 1);

    //正常金额
    if (OK == status) {
    return redPacket;
    }

    //如果生成的金额不合法 则递归重新生成
    if (LESS == status) {
    recursiveCount++;
    System.out.println("recursiveCount==" + recursiveCount);
    return randomRedPacket(ttalMoney, minMoney, redPacket, count);
    }

    if (MORE == status) {
    recursiveCount++;
    System.out.println("recursiveCount===" + recursiveCount);
    return randomRedPacket(totalMoney, redPacket, maxMoney, count);
    }

    return redPacket;
    }

    /**
    * 校验剩余的金额的平均值是否在 最小值和最大值这个范围内
    *
    * @param lastMoney
    * @param count
    * @return
    */
    private int checkMoney(int lastMoney, int count) {
    double avg = lastMoney / count;
    if (avg < MIN_MONEY) {
    return LESS;
    }

    if (avg > MAX_MONEY) {
    return MORE;
    }

    return OK;
    }


    public static void main(String[] args) {
    RedPacket redPacket = new RedPacket();
    List<Integer> redPackets = redPacket.splitRedPacket(20000, 100);
    System.out.println(redPackets);

    int sum = 0;
    for (Integer red : redPackets) {
    sum += red;
    }
    System.out.println(sum);
    }

    }






    注 引用至:github.com/crossoverJie
    lhx2008
        4
    lhx2008  
       Sep 28, 2019 via Android
    100 元,随机在 0-100 9 个数,加上 0 和 100,相邻的数距离有 10 个,就是 10 个红包
    Raymon111111
        5
    Raymon111111  
       Sep 28, 2019
    微信的逻辑是每次要抢的时候去随机生成一个合法的数字
    zhuzhibin
        6
    zhuzhibin  
    OP
       Sep 28, 2019 via iPhone
    @Baymaxbowen 老哥 我 ip 被 ban 了呜呜 解封后我倒回来看看
    zhuzhibin
        7
    zhuzhibin  
    OP
       Sep 28, 2019 via iPhone
    @lhx2008 如果刚好随机的情况是平均分配呢 ?每个人刚好 10 元
    lhx2008
        8
    lhx2008  
       Sep 28, 2019
    @zhuzhibin #7 没问题呀,随机到 0 10 20 30 40 50 60 70 80 90 100 就是平均分配了。要说 bug 就是随机数要去重,如果有重复要重新随机一个。
    Cbdy
        9
    Cbdy  
       Sep 28, 2019   1
    随手写了一个
    wysnylc
        10
    wysnylc  
       Sep 28, 2019
    红包算法都是预先生成,发红包的时候已经根据红包领取人数随机好了所有金额
    任何按照随机数来的都不可避免的可能会出现极大和极小值
    这不是算法问题,是业务问题
    x86
        11
    x86  
       Sep 28, 2019
    @Raymon111111 #5 我也感觉是这样每次点的时候拿剩余额度在随机一个
    snw
        12
    snw  
       Sep 28, 2019 via Android
    @wysnylc
    微信的红.包就不是这样。
    geelaw
        13
    geelaw  
       Sep 28, 2019 via iPhone   1
    https://geelaw.blog/entries/red-envelope/

    这个问题其实很无聊 - - 从我两个小时就能排版完毕就能看出来。
    w292614191
        14
    w292614191  
       Sep 28, 2019
    我记得,过年群里:80 元。
    我得了 79,另一个人 1 块钱。
    简直逆天。
    zhuzhibin
        15
    zhuzhibin  
    OP
       Sep 28, 2019
    @geelaw 老哥 感谢回复
    DevRoss
        16
    DevRoss  
       Sep 28, 2019 via Android
    分层抽样减少方差呗
    zhuchaowe
        17
    zhuchaowe  
       Sep 28, 2019 via iPhone
    微信的逻辑: random(0.01,0.5*restMoney)
    About     Help     Advertise     Blog     API     FAQ     Solana     3154 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 53ms UTC 13:12 PVG 21:12 LAX 06:12 JFK 09:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86