红包算法探讨

最近在做一些微信红包发放的功能,从而了解了一系列的红包算法。这里探讨一下。给你一定的金额,给发n个红包。

随机数发红包

使用随机数,每次先给红包塞入1分钱,然后在剩余的红包余额里面随机一个金额加入红包内。
缺点:不公平,越后面越亏,越后面的随机数越少,非常不平衡。抢红包抢到心态爆炸。

package 红包;

import java.util.Arrays;
import java.util.Random;

public class Test1 {
    /**
     * 越后面的随机数越少,非常不平衡。抢红包抢到心态爆炸。
     * @param args
     */
    public static void main(String[] args) {
        int[] a = divide(100,20);
        int max = 0;
        int second = 0;
        for (int item : a) {
            if (item > max)
                max = item;
            if (item <max && item >second)
                second = item;
            System.out.println(item);
        }
        System.out.println(max);
        System.out.println(second);


    }

    private static  int[] divide(double money, int n) {
        //验证参数合理校验
        int fen = (int)(money*100);
        if (fen < n || n < 1) {
            System.out.println("红包个数必须大于0,并且最小红包不少于1分");

         }
        int[] a = new int[n];
        //先给每个红包中塞入1分
        Arrays.fill(a, 1);
        fen -= n;
        Random r = new Random();
        while (fen>1) {
            //最后一次剩余fen=1,fen===0
            int f = r.nextInt(fen);
            int index = r.nextInt(a.length);
            a[index] += f;
            fen -= f;
        }
        if (fen > 0) {
            a[0] += fen;
        }
        return a;
    }
}

线段切割法

相当于把总金额都变成1分分的钞票,随机使用木板插入里面,保证木板不重叠以及里面至少有1分钱即可。
优点:公平
缺点:发个数少的话,真.随机,差距大。

package 红包;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import javax.swing.border.Border;

public class Test2 {
    /**
     * 比较公平的方式,线段切割法。
     * 当发的红包个数大的时候就感觉挺公平的。发的少浮动大。
     * @param args
     */
    public static void main(String[] args) {
        List<Integer> a = divide(1000,5000);
        int max = 0;
        int second = 0;
        int min = 0;
        for (int item : a) {
            if (item > max)
                max = item;
            if (item <max && item >second)
                second = item;
            if (item == 1) 
                min += 1;
//            System.out.println(item);
        }
        System.out.println(max);
        System.out.println(second);
        System.out.println("min的个数为" + min);


    }

    private static  List<Integer> divide(double money, int n) {
        //验证参数合理校验
        int fen = (int)(money*100);
        if (fen < n || n < 1) {
            System.out.println("红包个数必须大于0,并且最小红包不少于1分");
         }
        List<Integer> boards = new ArrayList<>();
        boards.add(0);
        boards.add(fen);
        //红包个数和板砖个数的关系
        while (boards.size() < n+1) {
            int index = new Random().nextInt(fen-1)+1;
            if (boards.contains(index)) {
                //保证板子的位置不相同
                continue;
            }
            boards.add(index);
        }

        //计算每个红包的金额,将两个板子之间的钱加起来
        Collections.sort(boards);
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < boards.size()-1; i++) {
            Integer e = boards.get(i+1) - boards.get(i);
            list.add(e);
        }
        return list;

    }
}

微信红包策略

符合正态分布,最少一分,最多2*平均值-1,但是保证了落差不会太大,心态没那么容易爆炸。

package 红包;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Test3 {
    // 发红包算法,金额参数以分为单位
    /** https://mp.weixin.qq.com/s/7yDbdKHJ3OmNw_015Jc8Cg
     * 除了最后一次,任何一次都少过人均两倍。但是保证了落差不会太大,心态没那么容易爆炸。
     * @param totalAmount 分
     * @param totalPeopleNum 发放个数
     * @return
     */
    public static List<Integer> divideRedPackage(Integer totalAmount,
            Integer totalPeopleNum) {

        List<Integer> amountList = new ArrayList<Integer>();
        Integer restAmount = totalAmount;
        Integer restPeopleNum = totalPeopleNum;
        Random random = new Random();
        for (int i = 0; i < totalPeopleNum - 1; i++) {
            // 随机范围:[1,剩余人均金额的两倍),左闭右开
            int amount = random.nextInt(restAmount / restPeopleNum * 2 - 1) + 1;
            restAmount -= amount;
            restPeopleNum--;
            amountList.add(amount);
        }
        amountList.add(restAmount);
        return amountList;
    }

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackage(10000, 20);
        for (Integer amount : amountList) {
            System.out.println("抢到金额:"
                    + new BigDecimal(amount).divide(new BigDecimal(100)));
        }
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值