场景题:如何设计一个抢红包随机算法

面试官:咱来写个算法题吧

设计一个抢红包的随机算法,比如一个人在群里发了100块钱的红包,群里有10个人一起来抢红包,每人抢到的金额随机分配。

1.所有人抢到的金额之和要等于红包金额,不能多也不能少。

2.每个人至少抢到1分钱。

3.最佳手气不超过红包总金额的90%

解题思路1:随机分配法

  • 钱的单位转换为分,每次在[1, leaveMoney]这个区间内随机一个值,记为r;
  • 计算一下剩余金额leaveMoney-r,剩余金额(单位:分)必须大于剩余人数,不然后面的人无法完成分配,例如10个人,有1个人抢了红包,剩余的money至少还需要9分钱,不然剩余的9人无法分;
  • 按照顺序随机n-1次,最后剩下的金额可以直接当做最后一个红包,不需要随机;

解题代码:

 public static List<Double> generate(double totalMoney, int people) {// 转换为分处理避免浮点误差double totalCents = Math.round(totalMoney * 100);double maxLimit = (totalCents * 0.9); // 总金额的90%double leaveMoney = totalCents;List<Double> result = new ArrayList<>();//判断钱不够分,不处理if ((int)totalCents < people) {return result;}Random random = new Random();//每次生成随机数int n = people - 1;while (n > 0) {//随机数在[1, min(maxLimit, leaveMoney)]之间,单位是:分double min = Math.min(leaveMoney, maxLimit);double allocResult = 1 + random.nextInt((int)min);//判断这次分配后,后续的总金额仍然可分,且不超过90%总金额if (allocResult > maxLimit || (leaveMoney - allocResult) < n) {continue;}leaveMoney -= allocResult;n--;result.add(allocResult / 100.0);}result.add(leaveMoney / 100.0);return result;}

以下是多次运行的结果:

[37.77, 50.76, 1.89, 7.89, 0.26, 0.24, 0.25, 0.78, 0.06, 0.1]
[89.38, 2.45, 3.5, 4.43, 0.03, 0.08, 0.06, 0.04, 0.01, 0.02]
[53.51, 40.86, 5.48, 0.04, 0.06, 0.01, 0.01, 0.01, 0.01, 0.01]
[42.71, 0.27, 38.99, 4.5, 4.02, 4.58, 2.97, 0.84, 0.21, 0.91]

通过多次运行的结果,可以看到越早抢红包的人,抢到的金额越大,所以题目还可以变形

要求红包金额分布均衡

面试官:继续改进红包生成算法,要求:

1.要保证红包拆分的金额尽可能分布均衡,不要出现两极分化太严重的情况。

解题思路2:二倍均值法

二倍均值法:假设剩余红包金额为m元,剩余人数为n,那么有如下公式:

每次抢到的金额 = 随机区间 [0.01,m /n × 2 - 0.01]元

这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。

举个例子如下:

假设有5个人,红包总额100元。100÷5×2 = 40,所以第1个人抢到的金额随机范围是[0.01,39.99]元,在正常情况下,平均可以抢到20元。

假设第1个人随机抢到了20元,那么剩余金额是80元。80÷4×2 = 40,所以第2个人抢到的金额的随机范围同样是[0.01,39.99]元,在正常的情况下,还是平均可以抢到20元。假设第2个人随机抢到了20元,那么剩余金额是60元。60÷3×2 = 40,所以第3个人抢到的金额的随机范围同样是[0.01,39.99]元,平均可以抢到20元。以此类推,每一次抢到金额随机范围的均值是相等的。

解题代码:

public static List<Double> allocateRedEnvelop(double totalMoney, int people) {// 转换为分处理避免浮点误差double totalCents = Math.round(totalMoney * 100);double maxLimit = (totalCents * 0.9); // 总金额的90%Random random = new Random();double leaveMoney = totalCents;List<Double> result = new ArrayList<>();int n = people;//注意是大于1,最后1个人领取剩余的钱while (n > 1) {//生成随机金额的范围是[1, leaveMoney / n * 2 - 1], 注意nextInt方法生成结果范围是左闭右开的double allocatMoney = 1 + random.nextInt((int)leaveMoney / n * 2 - 1);result.add(allocatMoney / 100.0);n--;leaveMoney -= allocatMoney;}result.add(leaveMoney / 100.0);return result;}

生成结果测试如下,结果值比较随机了,领取的红包金额和先后顺序无关了

[8.58, 4.56, 20.88, 13.83, 7.6, 3.94, 10.87, 8.66, 20.92, 0.16]
[3.31, 2.08, 15.99, 16.79, 13.13, 0.61, 17.38, 10.93, 4.93, 14.85]
[0.24, 21.86, 15.57, 16.86, 3.45, 3.18, 5.48, 13.01, 6.76, 13.59]

解题思路3:线段切割法

考虑一种新的解法,把红包总金额想象成一条很长的线段,而每个人抢到的金额就是这条主线段上的某个子线段,如下图:

在这里插入图片描述

  • 假设有N个人一起抢红包,红包总金额为M,就需要确定N-1个切割点;

  • 切割点的随机范围是(1,M),所有切割点确认后,子线段长度也就确定了

  • 如果随机切割点出现重复,则重新生成切割点

解题代码如下:

    /*** 线段切割法*/public static List<Double> allocateRedEnvelopNew(double totalMoney, int people) {// 转换为分处理避免浮点误差double totalCents = Math.round(totalMoney * 100);double maxLimit = (totalCents * 0.9); // 总金额的90%Random random = new Random();double leaveMoney = totalCents;List<Double> result = new ArrayList<>();Set<Integer> pointCutSet = new HashSet<>();int n = people;while (pointCutSet.size() < people - 1) {//生成n - 1个切割点,随机点取值范围是[1, totalCents]pointCutSet.add(random.nextInt((int) totalCents) + 1);}//接着生成对应子线段的钱数Integer[] points = pointCutSet.toArray(new Integer[0]);Arrays.sort(points);result.add(points[0] / 100.0);//子线段+ 最后那段的长度 = totalCents,注意上一步是已经加了points[0],result中的所有元素和累加后的结果一定是totalCents,for (int i = 1; i < points.length; i++) {result.add((points[i] - points[i - 1]) / 100.0);}result.add((totalCents - points[points.length - 1]) / 100.0);return result;}

最后跑几次看看生成的随机效果,可以看到手气最佳的有到37块钱的,相比较二倍均值法,该方法手气最佳获取的金额可能更高

[20.24, 3.9, 7.63, 9.62, 15.41, 2.32, 0.21, 24.94, 9.66, 6.07]
[8.64, 33.55, 3.76, 15.35, 4.41, 9.85, 4.81, 15.9, 2.71, 1.02]
[11.31, 13.32, 16.53, 5.91, 8.69, 17.29, 11.09, 7.62, 7.14, 1.1]
[21.34, 8.24, 1.9, 7.98, 0.49, 0.32, 13.75, 37.27, 0.03, 8.68]

以上就是关于红包随机算法的所有解题方法了,面试中如果遇到考这道算法题,需要问清楚红包随机的情况,有没有要求分布均衡。

如果觉得对面试有帮助的话,记得给文章点赞哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36433.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java开发经验——Throwable/Exception异常处理方式

摘要 文章主要探讨了 Java 开发中 Throwable 和 Exception 的异常处理方式。阿里巴巴 Java 开发手册规定&#xff0c;RPC 调用、二方包、动态代理类等场景推荐使用 Throwable&#xff0c;因为这些场景可能会出现类似 NoClassDefFoundError 这样的严重错误&#xff0c;使用 Thr…

[Mysql]创建数据库基础

数据库意义 更加利于管理的东西-数据库&#xff0c;他能有效的管理数据 举例一个生活化的案例说明 如果说&#xff0c;图书馆是保存书籍的&#xff0c;那么数据库技术保存数据的 数据库的简单原理图 Mysql数据库三层结构与本质 数据库管理系统与 mysqld&#xff1a;MySQL 数…

AMBA-CHI协议详解(二十五)

AMBA-CHI协议详解&#xff08;一&#xff09;- Introduction AMBA-CHI协议详解&#xff08;二&#xff09;- Channel fields / Read transactions AMBA-CHI协议详解&#xff08;三&#xff09;- Write transactions AMBA-CHI协议详解&#xff08;四&#xff09;- Other transac…

【RabbitMQ】RabbitMQ的基本架构是什么?包括哪些核心组件?

RabbitMQ基于AMQP协议实现&#xff0c;由多个核心组件组成&#xff0c;确保消息的可靠传递。 Rabbit的架构图&#xff1a; 1.RabbitMQ的基本架构&#xff1a; 1.核心组件&#xff1a; 1.Producer(生产者)&#xff1a; 发送消息到RabbitMQ。 2.Exchange(交换机)&#xff1a;接…

【PCB工艺】基础:电子元器件

电子原理图&#xff08;Schematic Diagram&#xff09;是电路设计的基础&#xff0c;理解电子元器件和集成电路&#xff08;IC&#xff09;的作用&#xff0c;是画好原理图的关键。 本专栏将系统讲解 电子元器件分类、常见 IC、电路设计技巧&#xff0c;帮助你快速掌握电子电路…

Html label标签中的for属性(关联表单控件:将标签与特定的表单元素(如输入框、复选框等)关联起来;提高可用性;无障碍性)

文章目录 示例代码for属性含义完整代码示例 示例代码 <div class"form-group"> <!-- 表单组&#xff0c;包含省份输入框和标签 --><label for"province">省份名称&#xff1a;</label> <!-- 省份输入框的标签 --><input…

S32K144外设实验(二):ADC单通道单次采样(软件触发)

文章目录 1. 概述1.1 理论回顾1.1.1 时钟系统1.1.2 采样通道1.2 实验目的2. 配置与代码编写1. 概述 1.1 理论回顾 S32K144的ADC应该说是特别灵活,笔者采用循序渐进的方式来学习使用这个很重要的外设。 在《入门笔记系列》专栏中对用户手册进行了翻译和解读,这里在回顾一下A…

进程控制~

一.进程控制 1.进程创建 我们可以通过./cmd来运行我们的程序&#xff0c;而我们运行的程序就是bash进程常见的子进程。当然我们也可以通过fork()系统调用来创建进程。 NAME fork - create a child process SYNOPSIS #include <unistd.h> pid_t fork(void…

经历过的IDEA+Maven+JDK一些困惑

注意事项&#xff1a;由于使用过程中是IDEA绑定好另外2个工具&#xff0c;所以报错统一都显示在控制台&#xff0c;但要思考和分辨到底是IDEA本身问题导致的报错&#xff0c;还是maven导致的 标准配置 maven Java Compiler Structure 编辑期 定义&#xff1a;指的是从open pr…

将bin文件烧录到STM32

将bin文件烧录到STM32 CoFlash下载生成hex文件hex2bin使用下载bin到单片机 CoFlash下载 选择需要安装的目录 在Config中可以选择目标芯片的类型 我演示的是 stm32f103c8t6 最小系统板 Adapter&#xff1a;烧录器类型 Max Clock&#xff1a;下载速度 Por&#xff1a;接口类型&am…

硬件基础(5):(2)二极管分类

文章目录 &#x1f4cc; 二极管的分类与详细介绍1. **整流二极管&#xff08;Rectifier Diode&#xff09;**特点&#xff1a;选型依据&#xff1a;补充说明&#xff1a; 2. **快恢复二极管&#xff08;Fast Recovery Diode&#xff09;**特点&#xff1a;选型依据&#xff1a;…

【MySQL】MySQL如何存储元数据?

目录 1.数据字典的作用 2. MySQL 8.0 之前的数据字典 3. MySQL 8.0 及之后的数据字典 4.MySQL 8 中的事务数据字典的特征 5.数据字典的序列化 6. .sdi文件的作用&#xff1a; 7..sdi的存储方式 在 MySQL 中&#xff0c;元数据&#xff08;Metadata&#xff09; 是描述数…

瑞萨RA系列使用JLink RTT Viewer输出调试信息

引言 还在用UART调试程序么?试试JLINK的RTT Viewer吧!不需占用UART端口、低资源暂用、实时性高延时微秒级,这么好的工具还有什么理由不用了! 目录 一、JLink RTT Viewer 简介 二、软件安装 三、工程应用 3.1 SEGGER_RTT驱动包 3.2 手搓宏定义APP_PRINT 3.3 使用APP_…

Ranger 鉴权

Apache Ranger 是一个用来在 Hadoop 平台上进行监控&#xff0c;启用服务&#xff0c;以及全方位数据安全访问管理的安全框架。 使用 ranger 后&#xff0c;会通过在 Ranger 侧配置权限代替在 Doris 中执行 Grant 语句授权。 Ranger 的安装和配置见下文&#xff1a;安装和配置 …

LabVIEW烟气速度场实时监测

本项目针对燃煤电站烟气流速实时监测需求&#xff0c;探讨了静电传感器结构与速度场超分辨率重建方法&#xff0c;结合LabVIEW多板卡同步采集与实时处理技术&#xff0c;开发出一个高效的烟气速度场实时监测系统。该系统能够在高温、高尘的复杂工况下稳定运行&#xff0c;提供高…

【系统架构设计师】操作系统 - 特殊操作系统 ③ ( 微内核操作系统 | 单体内核 操作系统 | 内核态 | 用户态 | 单体内核 与 微内核 对比 )

文章目录 一、微内核操作系统1、单体内核 操作系统2、微内核操作系统 引入3、微内核操作系统 概念4、微内核操作系统 案例 二、单体内核 与 微内核 对比1、功能对比2、单体内核 优缺点3、微内核 优缺点 一、微内核操作系统 1、单体内核 操作系统 单体内核 操作系统 工作状态 : …

人工智能之数学基础:线性方程组

本文重点 线性方程组是由两个或两个以上的线性方程组成的方程组,其中每个方程都是关于两个或两个以上未知数的线性方程。 记忆恢复 我们先从小学学习的线性方程组找到感觉 解答过程: 将第二个方程乘以2,得到: 2x−2y=2 将第一个方程减去新得到的方程,消去x: (2x+y)−…

​第十一届传感云和边缘计算系统国际会议

重要信息 时间地点&#xff1a;2025年4月18-20日 中国-珠海 会议官网&#xff1a;www.scecs.org 简介 第十一届传感云和边缘计算系统 (SCECS 2025&#xff09;将于2025年4月18-20日在中国珠海召开。将围绕“传感云”、“边缘计算系统”的最新研究领域&#xff0c;为来自国…

MDM设备管控,企业移动设备管理方案

目录&#xff1a; 目录 目录&#xff1a; 1. MDM&#xff1a;含义与定义 2. MDM如何工作&#xff1f; 3. BYOD与MDM&#xff1a;挑战与解决方案 4. 移动设备管理的主要优势 5. 移动设备管理的基本要素 6. 移动设备管理最佳实践 --地平线-- 移动设备管理 (MDM)历经多年…

S32k3XX MCU时钟配置

今天想从头开始配置S32K312中EB中的MCU模块&#xff0c;以下是我的配置思路与理解。 关键是研究明白&#xff0c;这些频率是如何通过一个总时钟&#xff0c;一步步分频得到的。 参考时钟&#xff0c;供外设模块使用&#xff0c;不同外设需要配置合理的参考时钟。 clock genera…