数据结构---红包分配算法

news2024/11/25 22:41:50

红包分配算法

  • 错误解法
  • 二倍均值法
    • JAVA实现
  • 线段切割法
    • 确定每一条子线段的长度
  • JAVA实现

问题如下:

  1. 所有人抢到的金额之和要等于红包金额,不能多也不能少。
  2. 每个人至少抢到1分钱。
  3. 要保证红包拆分的金额尽可能分布均衡,不要出现两极分化太严重的情况。

错误解法

每次拆分的金额 = 随机区间[1分, 剩余金额-1分]

存在的问题:

  1. 第1个人抢到金额的随机范围是[0.01,99.99]元,在正常的情况下,抢到金额的中位数是50元。假设第1个人随机抢到了50元,那么剩余金额是50元。
  2. 第2个人抢到金额的随机范围就小得多了,只有[0.01,49.99]元,在正常的情况下,抢到金额的中位数是25元。假设第2个人随机抢到了25元,那么剩余金额是25元。
  3. 第3个人抢到金额的随机范围就更小了,只有[0,24.99]元,按中位数可以抢到12.5元。
  4. 以此类推,红包的随机范围将会越来越小,不符合红包拆分的金额尽可能分布均衡的需求。。。

二倍均值法

把每次随机金额的上限定为剩余人均金额的2倍。

红包金额为m元
剩余人数为n
每次抢到的金额 = 随机区间 [0.01,m /n × 2 - 0.01]元

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

  1. 第1次随机的金额有一半概率超过20元,使得后面的随机金额上限不足39.99元;
  2. 第1次随机的金额同样也有一半的概率小于20元,使得后面的随机金额上限超过39.99元。
  3. 因此从整体来看,第2次随机的平均范围仍然是[0.01,39.99]元。

JAVA实现

    /**
     * 分配红包的金额
     * @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,剩余人均金额的2倍-1] 分
            //+1d的原因是:random.nextInt随机返回一个值在[0,num)的int类型的整数,包括0不包括num
            //random.nextInt+1的范围是[1,num+1)也就是[1,num]
            //就满足了随机区间: [1 , m /n × 2 - 1]分
            int amount = random.nextInt((restAmount/restpeopleNum)*2-1)+1;
            restAmount = restAmount-amount;
            restpeopleNum--;
            amountList.add(amount);
        }
        amountList.add(restAmount);
        return amountList;
    }

测试方法:

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackage(1000,10);
        for (Integer e:amountList){
            System.out.print(e+"  ");
        }
        System.out.println();
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

局限性
除最后一次外,其他每次抢到的金额都要小于剩余人均金额的2倍,并不是完全自由地随机抢红包。(随机数值的上限被限制死了

线段切割法

把红包总金额想象成一条很长的线段,而每个人抢到的金额,则是这条主线段所拆分出的若干子线段。

确定每一条子线段的长度

由“切割点”来决定。
当n个人一起抢红包时,就需要确定n-1个切割点。

当所有切割点确定以后,子线段的长度也随之确定。此时红包的拆分金额,就等同于每个子线段的长度

当n个人一起抢总金额为m的红包时,我们需要做n-1次随机运算,以此确定n-1个切割点。
随机的范围区间是[1, m-1]。
开始1和结束是m-1的原因是最少分配1分钱(这里1表示一分)
[0,1]一段,[m-1,m]一段(最小的情况)

需要考虑的问题:

  1. 当随机切割点出现重复时,如何处理。
  2. 如何尽可能降低时间复杂度和空间复杂度。

JAVA实现

HashMap和HashSet区别:HashSet存取值

/**
     * 拆分红包V2
     * @param totalAmount  总金额(以分为单位)
     * @param totalPeopleNum  总人数
     */
    public static List<Integer> divideRedPackageV2(Integer totalAmount, Integer totalPeopleNum){
        List<Integer> amountList = new ArrayList<Integer>();
        Set<Integer> segments = new HashSet<Integer>();
        Random random = new Random();
        for(int i = 0; i< totalPeopleNum-1; i++){
            //切割的位置segment,随机的范围区间是[1, m-1]
            int segment =  random.nextInt(totalAmount-2) + 1;
            //delta == 1
            int delta = random.nextInt(1)==0 ? 1 : -1;
            //这是异常的情况
            // segments.contains(segment) ==true 表示切割位置重复
            //segment == 0,切割位置不正确
            while(segments.contains(segment) || segment == 0){
                //加上1位,用来解决以上俩个问题。
                //%totalAmount是解决超过线段长度的情况。
                segment = (segment+delta)%totalAmount;
            }
            segments.add(segment);
        }

        //依据切割的段来划分 钱数
        //从HashSet取值
        //Collections.sort排序方法,把切割位置从小到大排序。
        List<Integer> segmentList = new ArrayList<Integer>(segments);
        Collections.sort(segmentList);
        for(int i=0; i<segmentList.size(); i++){
            Integer amount;
            if(i==0){
                amount = segmentList.get(0);
            }else {
                amount = segmentList.get(i) - segmentList.get(i-1) ;
            }
            amountList.add(amount);
        }
        amountList.add(totalAmount - segmentList.get(segmentList.size()-1));

        return amountList;
    }

测试方法:

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackageV2(1000,10);
        for (Integer e:amountList){
            System.out.print(e+"  ");
        }
        System.out.println();
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

保证了每次划分都是随机,并且概率相同,并且没有最大限制。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C函数】函数详解

函数前言一、函数是什么二、C语言中函数的分类&#xff08;一&#xff09;库函数1.printf类2.strcpy类3.math类4.概念5.小知识6.总结&#xff08;二&#xff09;自定义函数1.概念2.函数的组成3.例子1&#xff08;求出两个数中的最大值&#xff09;4.例子2&#xff08;交换两个整…

mac释放“其他”内存空间的解决方法

官方解释Mac设备储存空间中的“其他”数据包含这不可移除的移动资源&#xff0c;例如&#xff0c;Siri 语音、字体、词典、不可移除的日志和缓存、聚焦索引以及系统数据如钥匙串和 CloudKit 数据库、系统无法删除缓存的文件等之外&#xff0c;还包含了一些无法识别的文件。当“…

ROS2 基础概念 节点

ROS2 基础概念 节点1. Nodes2. 重映射3. 环境设置3.1. ROS_DOMAIN_ID3.2. ROS_LOCALHOST_ONLY1. Nodes 每个节点应负责单个模块用途&#xff08;例如&#xff0c;一个节点用于控制车轮电机&#xff0c;一个用于控制激光测距仪等&#xff09; 可以通过话题、服务、操作或参数向…

C++-----模板

举个例子&#xff0c;如果要你交换两个数值&#xff0c;你会怎么做呢&#xff1f; ————你肯定会说&#xff0c;那就写一个Swap交换函数吧&#xff01; 没错&#xff01;Swap函数确实可以实现交换&#xff0c;但如果我想让你同时进行不能类型的数值呢&#xff0c;比如floa…

F - Permutation Distance(去绝对值数据结构)[AtCoder Beginner Contest 283]

题目如下&#xff1a; 题目链接 题解 or 思路&#xff1a; 去掉绝对值后 有 2242 \times 2 4224 中情况 虚线括起来的是需要维护的&#xff0c;其他直接枚举就行! 对于 pi<pjp_i < p_jpi​<pj​ 的情况&#xff0c;设我们维护的式子为 xxx 那我们每次枚举查找的范围…

hadoop生产调优之HDFS—集群压测

在企业中非常关心每天从 Java 后台拉取过来的数据&#xff0c;需要多久能上传到集群&#xff1f;消费者关心多久能从 HDFS 上拉取需要的数据&#xff1f; 为了搞清楚 HDFS 的读写性能&#xff0c;生产环境上非常需要对集群进行压测。 HDFS 的读写性能主要受网络和磁盘影响比较大…

【matplotlib】3-绘制统计图形

文章目录绘制统计图形1.柱状图1.1 应用场景--定性数据的分布展示1.2 绘制原理2.条形图3.堆积图3.1 堆积柱状图3.2 堆积条形图4.分块图4.1 多数据并列柱状图4.2 多数据平行条形图5.参数探索6.堆积折线图、间断条形图和阶梯图6.1 用函数stackplot()绘制堆积折线图6.2 用函数broke…

Matlab 方位角计算

文章目录 一、简介二、实现代码三、实现效果一、简介 方位角是指从某点的正北方向起顺时针旋转到某目标点方向的水平夹角,角度范围(0~360)。如下所示: 令atn= a r c t a n ( Δ Y A B / Δ X

9. SpringMvc拦截器

1. 拦截器概念和作用 拦截器&#xff08;Interceptor&#xff09;是一种动态拦截方法调用的机制&#xff0c;在SpringMVC中动态拦截控制器方法的执行作用&#xff1a; 在指定的方法调用前后执行预先设定的代码阻止原始方法的执行总结&#xff1a;增强 核心原理&#xff1a;AOP…

[CG笔记]绘制图元:三角形

学习资料是Github的一个项目Tiny renderer or how OpenGL works: software rendering in 500 lines of code 本文对应原教程的第二课的部分内容 原教程重在思路&#xff0c;主要内容是以推导为主&#xff0c;所以这里还是记录思路和为代码做注释 知乎也有人给出了中译版&…

ARM uart stdio 的移植

一、uart stdio的移植1 1. 什么是 stdio (1) #include <stdio.h> (2) stdio&#xff1a;standard input output&#xff0c;标准输入输出 (3) 标准输入输出就是操作系统定义的默认的输入和输出通道。一般在 PC 机的情况下&#xff0c;标准输入指的是键盘&#xff0c;标…

C语言——操作符详解(下)

C语言——操作符详解&#xff08;下&#xff09;一、赋值操作符二、复合赋值符三、单目操作符单目操作符介绍四、 关系操作符五、逻辑操作符六、条件操作符七、逗号表达式八、下标引用、函数调用和结构成员8.1 [ ] 下标引用操作符8.2 ( ) 函数调用操作符8.3访问一个结构的成员一…

【Linux】Linux指令串讲

大家好&#xff0c;今天要开启一个新的专题&#xff1a;Linux 今天的内容是指令还有一些基本的Linux知识补充 由于Linux的知识很难明确写出分类&#xff0c;所以目录就不会做的特别详细完全 喜欢的小伙伴点赞收藏一下不迷路哦 目录 1.目录 2.文件 3.路径 1.目录 1.创建目录…

初识Docker:(3)Docker架构

初识Docker&#xff1a;&#xff08;3&#xff09;Docker架构镜像和容器Docker和DockerHubDocker架构总结镜像和容器 镜像&#xff08;Image&#xff09;&#xff1a;Docker将应用程序及其所需的以来、函数库、环境、配置等文件打包在一起&#xff0c;成为镜像。 容器&#x…

力扣27.移除元素(双指针算法)

题目描述&#xff1a; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考…

编译openssl支持libcurl的https访问

如果编译时不带openssl库那么无法访问https的网页&#xff0c;从网页端什么也获取不到。 在调用post请求访问翔云OCR人脸识别时无法访问&#xff0c;而使用ssl&#xff0c;需要先在系统中安装OpenSSL。 如下图安装说明&#xff1a; 所以将原先安装的libcurl库删掉&#xff0…

MySQL事务的隔离级别

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;MySQL事务的隔离级别 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加入: 林在闪…

【C与数据结构】——寒假提高每日练习Day2

一共16日的练习&#xff0c;分为选择题与编程题&#xff0c;涵盖了C语言所学以及数据结构的重点&#xff0c;以及一些秋招、春招面试的高频考点&#xff0c;难度会随着天数而上升。1-8day为C语言&#xff0c;9-16day为数据结构。 &#xff08;建议在电脑客户端进行&#xff0c…

docker中的数据卷

目录 1. 为什么使用数据卷 2. 数据卷基本操作 2.1 创建数据卷 2.2 查看数据卷 2.3 查看数据卷详细信息 2.4 数据卷删除 3. 数据卷的使用 3.1 先创建数据卷再挂载 3.2 直接挂载宿主机目录 3.3 只读数据卷 4. 数据卷容器 4.1 新建数据卷容器 4.2 新建一个容器来使用数…

Nacos学习笔记 (8)服务发现基础应用

1. 什么是服务发现 在微服务架构中&#xff0c;整个系统会按职责能力划分为多个服务&#xff0c;通过服务之间协作来实现业务目标。这样在我们的代码中免不了要进行服务间的远程调用&#xff0c;服务的消费方要调用服务的生产方&#xff0c;为了完成一次请求&#xff0c;消费方…