[!] 讨论: 随机红包算法问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhuzhibin
V2EX    问与答

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

  •  
  •   zhuzhibin 2019-09-28 10:31:29 +08:00 3347 次点击
    这是一个创建于 2237 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    举个例子了呗:

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

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

    17 条回复    2019-09-28 20:25:39 +08:00
    niknik
        1
    niknik  
       2019-09-28 10:37:25 +08:00
    一条绳子,九个结,每段长度,然后十段绳子随机分出去(自我猜测应该是这样的,嘻嘻)
    Baymaxbowen
        2
    Baymaxbowen  
       2019-09-28 10:38:48 +08:00
    memedahui
        3
    memedahui  
       2019-09-28 10:40:52 +08:00   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(totalMoney, 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  
       2019-09-28 10:40:53 +08:00 via Android
    100 元,随机在 0-100 9 个数,加上 0 和 100,相邻的数距离有 10 个,就是 10 个红包
    Raymon111111
        5
    Raymon111111  
       2019-09-28 10:41:44 +08:00
    微信的逻辑是每次要抢的时候去随机生成一个合法的数字
    zhuzhibin
        6
    zhuzhibin  
    OP
       2019-09-28 10:43:39 +08:00 via iPhone
    @Baymaxbowen 老哥 我 ip 被 ban 了呜呜 解封后我倒回来看看
    zhuzhibin
        7
    zhuzhibin  
    OP
       2019-09-28 10:48:48 +08:00 via iPhone
    @lhx2008 如果刚好随机的情况是平均分配呢 ?每个人刚好 10 元
    lhx2008
        8
    lhx2008  
       2019-09-28 10:53:14 +08:00
    @zhuzhibin #7 没问题呀,随机到 0 10 20 30 40 50 60 70 80 90 100 就是平均分配了。要说 bug 就是随机数要去重,如果有重复要重新随机一个。
    Cbdy
        9
    Cbdy  
       2019-09-28 11:19:29 +08:00   1
    随手写了一个
    wysnylc
        10
    wysnylc  
       2019-09-28 11:57:26 +08:00
    红包算法都是预先生成,发红包的时候已经根据红包领取人数随机好了所有金额
    任何按照随机数来的都不可避免的可能会出现极大和极小值
    这不是算法问题,是业务问题
    x86
        11
    x86  
       2019-09-28 11:58:05 +08:00
    @Raymon111111 #5 我也感觉是这样每次点的时候拿剩余额度在随机一个
    snw
        12
    snw  
       2019-09-28 12:45:51 +08:00 via Android
    @wysnylc
    微信的红.包就不是这样。
    geelaw
        13
    geelaw  
       2019-09-28 15:05:18 +08:00 via iPhone   1
    https://geelaw.blog/entries/red-envelope/

    这个问题其实很无聊 - - 从我两个小时就能排版完毕就能看出来。
    w292614191
        14
    w292614191  
       2019-09-28 16:38:06 +08:00
    我记得,过年群里:80 元。
    我得了 79,另一个人 1 块钱。
    简直逆天。
    zhuzhibin
        15
    zhuzhibin  
    OP
       2019-09-28 19:02:16 +08:00
    @geelaw 老哥 感谢回复
    DevRoss
        16
    DevRoss  
       2019-09-28 19:04:24 +08:00 via Android
    分层抽样减少方差呗
    zhuchaowe
        17
    zhuchaowe  
       2019-09-28 20:25:39 +08:00 via iPhone
    微信的逻辑: random(0.01,0.5*restMoney)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     917 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 20:10 PVG 04:10 LAX 12:10 JFK 15:10
    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