前些天,在CSDN的微信订阅号里看到了一篇关于抢红包的算法。微信原文链接如下:
http://mp.weixin.qq.com/s?src=11×tamp=1525155316&ver=849&signature=vG5ZSGeawMwaYdiV-KI5tWVVd-ZioSD9yjD8sVEdz8WgZPiN0ckqCFrj1dRarW4wRGNIVtDndK3zOD6rbLw60HBxjeZkR22n1qYUlBLOfgNcbzOTY8j*2mDhxb9tmUHh&new=1
这两天,数学建模算是告一段落,来看看这个算法吧。
假设红包的总金额为100元,10个人抢(都参与)。
对于小灰提出来的第一种方法,每次抢到的金额 = 随机区间 ( 0, 剩余金额 )。评论里很多人质疑第一种,觉得产生的金额随机且差别不大,先抢的人并不会优势很大。自己觉得应该是先抢得到的大金额的红包的几率更大,可既然有人质疑,那么就事实说话吧。
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class GrabRedEnvelope {
//发红包算法,金额参数以分为单位
public static List<Integer> grabRedEnvelope(int totalAmount,int totalPeopleNum){
List<Integer> amountList = new ArrayList<Integer>();
int restAmount=totalAmount;
//int restPeopleNum=totalPeopleNum;
Random random =new Random();
for(int i=0;i<totalPeopleNum-1;i++){
//剩下的人抢剩下的钱,每个人抢的不少于一分钱
int amount=random.nextInt(restAmount)+1;
restAmount-=amount;
//restPeopleNum--;
amountList.add(amount);
}
amountList.add(restAmount);
return amountList;
}
public static void main(String[] args) {
List<Integer> amountList =grabRedEnvelope(10000, 10);
for(int amount : amountList){
System.out.println("抢到金额:" + new BigDecimal(amount).divide(new BigDecimal(100)));
}
}
}
第一种方法,不需要变量剩余多少人,只是挨着分配剩余的钱数。运行了很多次,的确会出现,先抢的人得到的金额更大的情况。情况之一截图如下,(大多类似):
评论还有说,先随机出红包,再随机排序。那么出来的也只是对于上面金额差异很大的一组分配金额列表排序,还是有失公允。
再看看原文提到的二倍均值法。
剩余红包金额为M,剩余人数为N,则每次抢到的金额 = 随机区间 (0, M / N X 2)。它保证了每次每次随机金额的平均值是相等的。可是随机自由度低,算法原文已经很清晰了,不再赘述。
提出的第三种方法是线段切割法。当N个人一起抢总金额为M的红包时,我们需要做N-1次随机运算,以此确定N-1个切割点。随机的范围区间是(1, M)。原文没有给出算法,我试着写了一下
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
public class GrabRedEnvelope {
//发红包算法,金额参数以分为单位,线段分割法O(∩_∩)O
public static List<Integer> grabRedEnvelope(int totalAmount,int totalPeopleNum){
List<Integer> amountPointList = new ArrayList<Integer>();
List<Integer> sortedAmountList = new ArrayList<Integer>();
int restAmount=totalAmount;
Random random =new Random();
int i=0;
//n-1个不同切割点
while(i<totalPeopleNum-1){
//随机n-1个切割点
System.out.println("抢到金额");
int point=random.nextInt(restAmount)+1;
restAmount-=point;
amountPointList.add(point);
i++;
if(amountPointList.size()>0 ){
for(int j=0;j<amountPointList.size();j++)
{
if (amountPointList.get(j) == point) {
amountPointList.remove(i);
i--;
}
}
}
}
//对切割点进行排序
Collections.sort(amountPointList);
Collections.sort(amountPointList, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return new Double((String) o1).compareTo(new Double((String) o2));
}
});
System.out.println(amountPointList.toString());
//输出最终结果
for(int temp=0;temp<amountPointList.size();temp++){
if(temp==0)
sortedAmountList.add(amountPointList.get(temp));
else
sortedAmountList.add(amountPointList.get(temp)-amountPointList.get(temp-1));
}
return sortedAmountList;
}
public static void main(String[] args) {
List<Integer> amountList =grabRedEnvelope(10000, 10);
for(int amount : amountList){
System.out.println("抢到金额:" + new BigDecimal(amount).divide(new BigDecimal(100)));
}
}
}
O(∩_∩)O哈哈~果然有错误,改天改吧o(╥﹏╥)o