抢红包算法(公平版和手速版)

举报
牛哄哄的柯南 发表于 2021/05/26 01:26:52 2021/05/26
【摘要】 当我们在群里抢红包时真的是手速越快红包金额越大吗? 答案当然是并不是,都说了是拼手气,岂能是拼手速! 不过也可以有拼手速的方法 抢红包 二倍均值法(公平版)线段切割法(手速版) 二倍均值法(公平版) 这是一种很合理很公平的抢红包算法了,绝对不会让你拼手速的,就别天真了。 在此我们假设 红包剩余金额为 M 红包剩余数量为 N 这种算法就是每次都在区间...

当我们在群里抢红包时真的是手速越快红包金额越大吗?
在这里插入图片描述
答案当然是并不是,都说了是拼手气,岂能是拼手速!
不过也可以有拼手速的方法
在这里插入图片描述

二倍均值法(公平版)

这是一种很合理很公平的抢红包算法了,绝对不会让你拼手速的,就别天真了。
在此我们假设
红包剩余金额为 M
红包剩余数量为 N
这种算法就是每次都在区间[0,M/N×2] 随机取一个数

假设100元红包发10个人,那么合理的做法应该是每个人领到10元的概率相同。
第一个人随机金额的范围为[0,100/10×2] ,也就是[0,20],这样平均可以领到10元,此时剩余金额为100-10=90。
第二个人随机金额的范围为[0,90/9×2] ,也就是[0,20],这样平均也可以领到10元,此时剩余金额为90-10=80。
第三个人随机金额的范围为[0,80/8×2] ,也就是[0,20],这样平均也可以领到10元。
这样推导下去,每个人领到相同金额的概率应该就是相同的了。

代码:

public static List<Double> doubleMeanMethod(double money,int number){
		List<Double> result = new ArrayList<Double>();
		if(money<0&&number<1) return null;
		double amount,sum=0;
		int remainingNumber=number;
		int i=1;
		while(remainingNumber>1){ amount= nextDouble(0.01,2*(money/remainingNumber)); sum+=amount; System.out.println("第"+i+"个人领取的红包金额为:"+format(amount)); money -= amount; remainingNumber--; result.add(amount); i++;
		}
		result.add(money);
		System.out.println("第"+i+"个人领取的红包金额为:"+format(money));
		sum+=money;
		System.out.println("验证发出的红包总金额为:"+format(sum)); return result; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行结果:
在这里插入图片描述
在这里插入图片描述

线段切割法(手速版)

这就是拼手速了,是时候展示手速了。
在这里插入图片描述

这个算法可以把总金额想象成一条线段,每个人都有机会切一刀,前面的人切剩下的后面的人再接着切,这样越是前面的人截取的长度(理解成领取到的红包金额)越大的概率就越大。

代码:

public static void lineSegmentCutting(double money,int number){
		if(money<0&&number<1) System.out.println("输入错误!");
		double begin=0,end=money;
		double y=0;
		for(int i=0;i<number-1;i++){ double nn=0; double amount=nextDouble(begin,end); nn=amount-begin; System.out.println("第"+(i+1)+"个人领取的红包金额为:"+format(nn)); y+=nn; begin=amount; }
		System.out.println("第"+number+"个人领取的红包金额为:"+format(end-begin));
		y+=(end-begin);
		System.out.println("验证发出的红包总金额为:"+format(y)); }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

运行结果:
在这里插入图片描述
在这里插入图片描述

完整代码:

package com.zhl.blogTest;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class redEnvelope { /*Random 随机生成一个区间在[min , max]的数值
	randNumber 将被赋值为一个 MIN 和 MAX 范围内的随机数
	 int randNumber =rand.nextInt(MAX - MIN + 1) + MIN; */ /**
	 * 生成min到max范围的浮点数
	 **/
	public static double nextDouble(final double min, final double max) {
		return min + ((max - min) * new Random().nextDouble());
	} public static String format(double value) { return new java.text.DecimalFormat("0.00").format(value); // 保留两位小数
		} //二倍均值法
	public static List<Double> doubleMeanMethod(double money,int number){
		List<Double> result = new ArrayList<Double>();
		if(money<0&&number<1) return null;
		double amount,sum=0;
		int remainingNumber=number;
		int i=1;
		while(remainingNumber>1){ amount= nextDouble(0.01,2*(money/remainingNumber)); sum+=amount; System.out.println("第"+i+"个人领取的红包金额为:"+format(amount)); money -= amount; remainingNumber--; result.add(amount); i++;
		}
		result.add(money);
		System.out.println("第"+i+"个人领取的红包金额为:"+format(money));
		sum+=money;
		System.out.println("验证发出的红包总金额为:"+format(sum)); return result; } //线段切割法
	public static void lineSegmentCutting(double money,int number){
		if(money<0&&number<1) System.out.println("输入错误!");
		double begin=0,end=money;
		double y=0;
		for(int i=0;i<number-1;i++){ double nn=0; double amount=nextDouble(begin,end); nn=amount-begin; System.out.println("第"+(i+1)+"个人领取的红包金额为:"+format(nn)); y+=nn; begin=amount; }
		System.out.println("第"+number+"个人领取的红包金额为:"+format(end-begin));
		y+=(end-begin);
		System.out.println("验证发出的红包总金额为:"+format(y)); } public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("这是一段模拟抢红包的代码。"); int number;
		double money;
		System.out.print("请输入红包总金额:");
		money = sc.nextDouble();
		System.out.print("请输入红包数量:");
		number = sc.nextInt();
		//System.out.println(money + " " + number); //二倍均值法
		doubleMeanMethod(money,number);
		//System.out.println(doubleMeanMethod(money,number).toString());
		//也是可以直接输出list的,为了观察方便,我就在循环中输出了,存在list里主要是为了后续方便数据的使用
		System.out.println(); //线段切割法
		lineSegmentCutting(money,number); }

}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

读到这里了,你真是个小可爱。不妨点个赞再走吧。
在这里插入图片描述
加油!

共同努力!

Keafmd

文章来源: keafmd.blog.csdn.net,作者:牛哄哄的柯南,版权归原作者所有,如需转载,请联系作者。

原文链接:keafmd.blog.csdn.net/article/details/108586841

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。