博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java抢红包线段分割法_抢红包的三种算法
阅读量:6376 次
发布时间:2019-06-23

本文共 3613 字,大约阅读时间需要 12 分钟。

首先来看一下抢红包的基本要求:

所有人抢到金额之和等于红包金额,不能超过,也不能少于。

每个人至少抢到一分钱。

要保证所有人抢到金额的几率相等。

下面来看具体的算法实现。

第一种算法

每个人每次抢到的金额范围是:(0,剩余金额)。

不过这个算法思路会违背“每个人分的金额随机,不能差距太大”。

为什么会这样,举个例子:

假设有10元钱,10个人分。

第一个人的随机范围是(0,10),平均分到5元。

假设第一个人随机分到5元,剩余金额为10-5=5元。

第二个人的随机范围是(0,5),平均分到2.5元。

假设第二个人随机分到2.5元,剩余金额为5-2.5=2.5元。

第三个人的随机范围是(0,2.5),平均分到1.25元

以此类推,每一次随机范围越来越小,违背了“每个人分的金额随机,不能差距太大”。

具体实现代码如下:

import java.math.BigDecimal;

import java.util.ArrayList;

import java.util.List;

import java.util.Random;

/**

* @author god-jiang

* @date 2020/1/26 16:49

*/

public class Algorithm {

public static List divideRedPackage(Integer totalAmount, Integer totalPeopleNum) {

List amountList = new ArrayList<>();

Integer restAmount = totalAmount;

Integer restPeopleNum = totalPeopleNum;

Random random = new Random();

for (int i = 0; i < totalPeopleNum - 1; i++) {

int amount = random.nextInt(restAmount - restPeopleNum - 1) + 1;

restAmount -= amount;

restPeopleNum--;

amountList.add(amount);

}

amountList.add(restAmount);

return amountList;

}

public static void main(String[] args) {

List amountList = divideRedPackage(1000, 10);

for (Integer amount : amountList

) {

System.out.println("抢到金额" + new BigDecimal(amount).divide(new BigDecimal(100)));

}

}

}

第二种算法:二倍均值法

思路

每次抢到的金额=随机范围(0,M/N *2)

M表示剩余红包金额,N表示剩余人数。这个公式保证了每次随机金额的平均值是相等的。

举个例子:

假设有10元钱,10个人分:

10/10 *2=2,所以第一个人分到的范围是(0,2),平均可以分到1元。

假设第一个人随机分到1元,那么剩余金额是10-1=9元。

9/9 *2=2,所以第二个人分到的范围是(0,2),平均可以分到1元。

假设第二个人随机分到1元,那么剩余金额是9-1=8元。

8/8 *2=2,所以第三个人的随机范围也是(0,2),平均可以分到1元。

以此类推,每一次随机范围都是相等的。

相关代码实现如下:

import java.math.BigDecimal;

import java.util.ArrayList;

import java.util.List;

import java.util.Random;

/**

* @author god-jiang

* @date 2020/1/26 15:39

*/

public class Algorithm {

public static List divideRedPackage(Integer totalAmount, Integer totalPeopleNum) {

List amountList = new ArrayList<>();

Integer restAmount = totalAmount;

Integer restPeopleNum = totalPeopleNum;

Random random = new Random();

for (int i = 0; i < totalPeopleNum - 1; i++) {

//保证金额范围是[1,剩余金额2倍),左闭右开

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 amountList = divideRedPackage(1000, 10);

for (Integer amount : amountList

) {

System.out.println("抢到金额" + new BigDecimal(amount).divide(new BigDecimal(100)));

}

}

}

这种算法其实也有弊端,比如经常会有人使用1块钱发100份的情况,或者1.01发100份的情况,此时最后抢的金额始终是2分。

另外,除了最后一次,任何一次抢到的红包的金额都小于人均金额的两倍,并不是任意随机的。

第三种方案:线段切割法

何谓线段切割法?我们可以把红包总金额想象成一条很长的线段,而每个人抢到的金额,则是这条主线段所拆分出的若干子线段。

如何确定每一条子线段的长度呢?由“切割点”来决定。当 N 个人一起抢红包的时候,就需要确定 N-1 个切割点。因此,我们需要做 N-1 次随机运算,以此确定 N-1 个切割点。随机的范围区间是(1, M)。

当所有切割点确定以后,子线段的长度也随之确定。这样每个人来抢红包的时候,只需要顺次领取与子线段长度等价的红包金额即可。

这就是线段切割法的思路。在这里需要注意以下两点:

当随机切割点出现重复,如何处理。

如何尽可能降低时间复杂度和空间复杂度。

相关实现代码如下:

public static List lineCut(int money, int people) {

List teams = new ArrayList<>(people - 1);

List result = new ArrayList<>(people);

Random random = new Random();

while (teams.size() < people - 1) {

int point = random.nextInt(money - 1) + 1;

if (!teams.contains(point)) {

teams.add(point);

}

}

Collections.sort(teams);

System.out.print("分割点:");

System.out.println(teams);

int left = 0;

for (Integer integer : teams) {

result.add(integer - left);

left = integer;

}

result.add(money - left);

System.out.print("每人金额:");

System.out.println(result);

// 验证分割后的数是否是输入的总金额

Optional r = result.stream().reduce(Integer::sum);

System.out.print("总金额:");

System.out.println(r.get());

return result;

}

小结

其实,如果你的项目中对指定的红包有特殊的要求,还可结合将三种算法结合起来进行使用。比如部分红包采用第二种,剩余红包采用第三种的形式来实现。

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

你可能感兴趣的文章
HTTP协议漫谈 --笔记
查看>>
react实现点击某个元素之外自动隐藏此元素
查看>>
load mainaccount
查看>>
iOS 应用内付费(IAP)开发步骤
查看>>
REST 在 Java 中的使用
查看>>
CentOS of MySQL command
查看>>
使用SHFB(Sandcastle Help File Builder)建立MSDN风格的代码文档
查看>>
AngularJS 服务(Service)
查看>>
devstack查看服务日志
查看>>
Fireworks Extension —— AutoSlice 介绍
查看>>
ABBYY FineReader错误代码142和55
查看>>
寄存器冲突的问题
查看>>
西楚霸王后面的女人如果是吕雉,楚汉争霸会是何结果?
查看>>
高手详解SQL性能优化十条经验
查看>>
【DOM编程艺术】图片库最终版
查看>>
Datable 添加到Dataset 并且重新命名
查看>>
响应式布局和自适应的区别
查看>>
Tomcat目录
查看>>
ProGet – Local Cache Package Server For Nuget
查看>>
struts2 ajax jquery返回json类型
查看>>