抢红包算法--四种抢红包算法对比

2023-05-16

        线上测试服务器中,有个同事做的抢红包算法被要求优化,大概听了下他们的讨论,最后的结果竟然要用什么概率论等等一系列我听过的、没听过的名词去解决。我表示一脸懵*。其实解决的问题就是一个:抢红包算法不够平均,先抢后抢有概率造成金额差值过大。

        针对这个问题的解决方法,有四种(普通法,线段切割法,双倍随机法,投篮球法)。前三种算法,网上基本都在流传,投篮球算法,是我自己瞎起的名字。这个算法还是因为当年校招,面试北京涂鸦移动,面试官现场引导我一个问题,用了投篮球这个例子。而我写这篇博客的时候,想起那个算法,蛮适用的,因而叫他投篮球算法。

        一、普通法:每次针对剩余总金额做一次随机,随机值就是第一个人的数值。这个算法也就是这个同事之前写的要求被优化的算法。这个算法的好处是完全随机,坏处是极有可能造成前人抢的太多,后人太少。例如,100元分给10个人,第一个人在0-100随机,均值为50。而第二个人的均值只剩25,之后是12.5。

//普通法    有保底
//iTotalGold总金额    iNum份数    iBaseGold保底金额
void oldThink(int iTotalGold, int iNum, int iBaseGold)
{
	if (iBaseGold*iNum > iTotalGold)
	{
		cout << "保底太多 " << iBaseGold<<endl;
		return;
	}
	iTotalGold -= (iBaseGold *iNum);
	std::vector<int> veGold(iNum);
	for (int i = 0; i < iNum-1; ++i)
	{
		int iAddNum = 0;
		if(iTotalGold != 0)
			iAddNum += rand() % (iTotalGold);
		veGold[i] = iAddNum + iBaseGold;
		iTotalGold -= iAddNum;
	}
	veGold[iNum - 1] = iTotalGold + iBaseGold;
	cout << "普通法:" << endl;
	copy(veGold.begin(), veGold.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
} 

        二、线段切割法:将总金额想象成一条那么长的线段,需要分割成num份,随机num-1次,将每次的随机值映射到该线段上。这样的好处是将随机交给程序,缺点是有小概率造成某个人分配过多。例如,100个人分10个红包,我们除了需要考虑随机值重合之外,每次完全随机,可能造成不够随机的情况。

//线段切割法    无保底
//iTotalGold总金额    iNum份数    iBaseGold保底金额
void cutline(int iTotalGold, int iNum)
{
	if (iNum > iTotalGold)
	{
		return;
	}
	if (iNum == iTotalGold)
	{
		for (int i = 0; i < iNum; ++i)
			cout << 1 << " ";
		cout << endl;
		return;
	}
	std::set<int> setGold;
	for (int i = 0; i < iNum-1; ++i)
	{
		while (1)
		{
			int iPos = rand() % iTotalGold;
			if (setGold.find(iPos) == setGold.end())
			{
				setGold.insert(iPos);
				break;
			}
		}
	}
	cout << "线段切割法(无保底):" << endl;
	int iPreLine = 0;
	for (auto &it : setGold)
	{
		cout << it - iPreLine << " ";
		iPreLine = it;
	}
	cout << iTotalGold - iPreLine << " ";
	cout << endl;
}

         三、双倍随机法:每次随机的时候,取0-每个人平均金额的2倍进行随机。这样已经基本完成了我们想要的样子,不错的方法。例如:100个人分10分,每次随机都是0-100/10*2去随机,基本可以保证每个人平均在10左右,是相当平均的算法。不过需要注意最后几个人可能已经不足20的情况。

//双倍随机法
//iTotalGold总金额    iNum份数    iBaseGold保底金额
void twobase(int iTotalGold, int iNum, int iBaseGold)
{
	if (iNum *iBaseGold > iTotalGold)
	{
		cout << "保底太多 " << iBaseGold << endl;
		return;
	}
	std::vector<int> veGold(iNum, iBaseGold);
	iTotalGold -= iNum*iBaseGold;
	int iBaseTmp = iTotalGold / iNum * 2;
	for (int i = 0; i < iNum - 1; ++i)
	{
		if (iTotalGold == 0)
			break;
		int iTmp = 0;
		if (iTotalGold >= iBaseTmp)
			iTmp = rand() % iBaseTmp;
		else
			iTmp = rand() % iTotalGold;
		veGold[i] += iTmp;
		iTotalGold -= iTmp;
	}
	veGold[iNum - 1] = iTotalGold;
	cout << "双倍随机法:" << endl;
	copy(veGold.begin(), veGold.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
}

       四、投篮球法:之前总是用金额去除以人数num以寻求平均。而投篮球法,则是以金额的单元值最为基准。以上三种算法都在极力的寻求随机,而投篮球法则是为了保证完全平均。例如:100个人分10分,每次取金额的最小单元值,比如一块钱,然后把10个人当成篮筐,一块钱当成篮球,每次都去投篮。这样的好处是不用模仿完全随机,他本身就是完全随机,坏处也很明显,循环次数过多。所以这里采用最小金额的单元值,例如每次取三块、五块。代码很简单:

//投篮球法    有保底
//iTotalGold总金额    iNum份数    iBaseGold保底金额
void basketball(int iTotalGold, int iNum, int iBaseGold)
{
	if (iBaseGold*iNum > iTotalGold)
	{
		cout << "保底太多 " << iBaseGold << endl;
		return;
	}
	iTotalGold -= (iBaseGold *iNum);
	std::vector<int> veGold(iNum, iBaseGold);
	for (int i = 0; i < iTotalGold; i += 2) //这里基准值用了2,减少循环次数
	{
		int iPos = rand() % iNum;
		veGold[iPos] += 2;
	}
	cout << "投篮球法:" << endl;
	copy(veGold.begin(), veGold.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
}

测试结果:

        测试了几次,目前最差的就是普通法,因为所有大金额全部集中在前几个人。双倍随机法和投篮球法基本上都在平均值附近,差值并非很大,但考虑到调用次数的话,双倍随机法生出,如果考虑到代码简洁的话,投篮球法稍好。线段切割法不适用于我们游戏中策划的需求,但是差值大一些的抢红包应该更适用于平时一些简单APP的应用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

抢红包算法--四种抢红包算法对比 的相关文章

  • ftp三种用户权限设置

    修改配置文件vsftpd conf Vi etc vsftpd vsftpd conf 修改匿名用户为禁止 2在禁止登录名单里删除root vi etc vsftpd user list vi etc vsftpd ftpuser 然后按d
  • 常见的socket出错总结

    常见错误 ECONNREFUSED 111 没有这个端口 EAGAIN 11 buff已满 EPIPE 32 客户端断掉了 ECONNRESET xff08 104 xff09 客户端先可以正常连接服务端 xff0c 并可以进行数据收发 x
  • CentOS-8中安装JDK 1.8

    备忘录 xff1a 喜欢 xff0c 即可 xff0c 无它 本例环境 xff1a 操作系统 xff1a CentOS 8 1 1911 x86 64 dvd1 安装包 xff1a jdk 8u251 linux x64 rpm 远程连接工
  • Android 系统(125)---Android通过Dialer实现暗码启动

    Android通过Dialer实现暗码启动 目前接触比较多的就是通过dialer应用来启动 触发暗码 本文以Dialer为例 xff0c 1 经过调试定位 xff0c 发现拨号盘接对应的Activity为DialtactsActivity
  • 阿里程序员常用的 15 个高效工具,大部分已开源!

    阿里程序员常用的 15 个高效工具 xff0c 大部分已开源 xff01 阿里将自身在各类业务场景下的技术积淀 xff0c 通过开源 云上实现或工具等形式对外开放 xff0c 本文将精选了一些阿里巴巴的开发者工具 xff0c 希望能帮助开发
  • Linux域名解析(DNS)

    DNS简介 域名系统 xff08 英文 xff1a Domain Name System xff0c 缩写 xff1a DNS xff09 xff0c 使用应用层协议 xff0c 是互联网的一项服务 它作为将域名和IP地址相互映射的一个分布
  • redhat6.7系统突然异常死机问题处理

    redhat6 7正常使用过一段时间之后异常死机 xff0c cat var log messages查看日志没有明显的error报错 xff0c 看带外管理日志发现是系统的问题 xff0c 后来通过修改grub conf配置解决了 xff
  • 下载并构建PX4

    根据官方的文档 xff0c PX4下载和构建的方式有两种 xff1a Linux系列的Console模式 xff08 当然也支持Windows下的MINGW32 xff09 和Windows模式 在Windows平台下 xff0c 我们习惯
  • 又是一年年终时...

    今年的第一天 xff0c 也就是 2009 年的第一天 xff0c 我用一个懒觉迎接了 2009 xff0c 整整睡到了中午 11 30 才醒 新一年初 xff0c 也就是明天 xff0c 我决定用早起来迎接 习惯了晚上学习 xff0c 早
  • 前端传值(枚举类接收问题)

    最近做的这个项目中 xff0c 用到了大量的枚举类 xff0c 今天来记录一下我遇到的问题 xff0c 如果能帮到大家就更好了 xff01 1 枚举类如何转为json xff08 在一个类的属性中 xff0c 这个枚举类属性如何直接使用在接
  • APM_ArduCopter源码解析学习(四)——IMU

    APM ArduCopter源码解析学习 xff08 四 xff09 IMU 前言一 system cpp 1 1 无人机内部初始化1 2 Copter init ardupilot 1 3 Copter startup INS groun
  • 查看当前系统的glibc版本

    有时我们经常需要查看当前系统的glibc版本 xff0c 可以这样查看 lib libc so 6 有时 lib x86 64 linux libc so 6 把这个文件当命令执行一下 为什么这个库可以直接run呢 xff1f 原来在lib
  • Java多线程学习三:有哪几种实现生产者消费者模式的方法

    我们先来看看什么是生产者消费者模式 xff0c 生产者消费者模式是程序设计中非常常见的一种设计模式 xff0c 被广泛运用在解耦 消息队列等场景 在现实世界中 xff0c 我们把生产商品的一方称为生产者 xff0c 把消费商品的一方称为消费
  • 如何在 Ubuntu 中管理和使用逻辑卷管理LVM

    在我们之前的文章中 xff0c 我们介绍了什么是 LVM 以及能用 LVM 做什么 xff0c 今天我们会给你介绍一些 LVM 的主要管理工具 xff0c 使得你在设置和扩展安装时更游刃有余 正如之前所述 xff0c LVM 是介于你的操作
  • 如何获取本地和远程主机的IP及MAC地址

    这篇文章 xff0c 我们不准备大规模的讨论技术问题 只是向大家介绍一下我们将如何获得一台主机的IP地址 在Win32 API中我们可以使用NetWork API完成这项工作 xff0c 但是在 Net平台下我们应当如何做呢 xff1f 其
  • 聊聊java中一些减少if-else 的编码方式!

    01 前言 前段时间在阅读别人所写的代码的时候 发现其中一些业务相关的方法体内 出现了比较多的if else语句多层嵌套的情况 首先我个人不是不提倡写if else语句 不得不说 很多时候 在写某些逻辑 使用if else 去做判断 代码看
  • 如何用简单方法推导正弦函数的和角公式: sin(α+β)=sinαcosβ+cosαsinβ ?

    问题 xff1a 看2014年湖北省高考理科数学题 xff0c 选择题第6题 xff1a 这道题目答案是C xff0c 组是正交函数 xff0c 组不是正交函数 可以用数形结合方式 xff0c 快速做出判断 详细解析如下 分析 xff1a
  • Http头部参数:Authorization

    项目uu约优中 xff0c 用到了头部Authorization 当时传递的参数也是后端返回的20位字符 项目sxaik中 xff0c http请求的头部传递Authorization xff0c 值为32位小写字符 xff0c 不确定是m
  • 高性能计算

    信息时代的硬件芯片和存储器价格以摩尔定律的形式下降 xff0c 可是现在处理的数据量也越来越大 我们先以cocoa编程为例 xff0c 然后再结合网格计算 云计算 xff0c 综合对最新的高性能计算技术作介绍 使用 runloop 在coc
  • @Documented注解的作用

    目录 在哪里用到了 96 64 Documented 96 注解 xff1f 那么 64 Documented的作用是什么 xff1f 在哪里用到了 64 Documented注解 xff1f 64 Documented是元注解 xff0c

随机推荐

  • 球的表面积公式是怎么推导出来的?

    球的体积公式的推导 球的表面积公式是 xff1a 证明方式一 xff1a 体积求导 基本思路 xff1a 可以把半径为R的球 xff0c 从球心到球表面分成n层 xff0c 每层厚为 r n xff0c 像洋葱一样 半径获得增量是 r xf
  • ViewBinding简单使用

    官方文档 xff1a https developer android google cn topic libraries view binding hl 61 zh cn java 在app module下的build gradle文件中
  • Android广播实现进程间通信,很简单

    应用A发送广播 xff1a span class token keyword public span span class token keyword class span span class token class name MainA
  • 下载JDK8 JVM源码

    性子急的可以直接看快速下载步骤 xff1a 目录 详细步骤快速下载步骤 详细步骤 打开openJDK官网 xff1a https openjdk org 找到左侧的Mercurial xff0c 点击进入新界面 选择jdk8 xff0c 点
  • Git查看分支的创建人

    开发小组人多的时候 xff0c 仓库里会有跟多分支 xff0c 需要看下某个分支具体是谁创建的 命令 xff1a git for each ref format 61 39 committerdate 09 authorname 09 re
  • kotlin的this关键字几种用法

    与java不同的是 xff0c 原先MainActivity this这种写法在kotlin中会报错 如下 正确的写法有许多 xff0c 直接就写this也可以识别到 xff0c 如下 xff1a span class token clas
  • kotlin中匿名内部类的写法

    原本java开发安卓常用的setOnClickListener xff0c 用kotlin写 xff0c 也变得五花八门了 span class token keyword var span view span class token op
  • Spring与SpringMVC的区别和联系是啥?

    Spring Spring是一个开源容器框架 xff0c 可以接管web层 xff0c 业务层 xff0c dao层 xff0c 持久层的组件 xff0c 并且可以配置各种bean 和维护bean与bean之间的关系 其核心就是控制反转 I
  • “在XML文件中给代码加注释”请注意注释的位置

    先科普一下eclipse加注释的快捷键 xff1a eclipse中编辑Java文件时 xff0c 注释和取消注释的快捷键都是 xff1a 34 CTRL 43 34 编辑xml文件时 xff0c 注释 xff1a CTRL 43 SHIF
  • HTTP代理服务器的实现

    接下来都是我对HTTP代理服务器的理解 HTTP代理服务 xff08 proxy server xff09 器就是客户端也是服务端 xff0c 是一个事务处理的中间人 xff0c 就像下图所展示的一样 xff0c 图片来源于 HTTP权威指
  • “无法识别的USB设备”如何解决

    昨天 xff0c 我把USB数据线插入笔记本电脑做真机调试 xff0c 电脑右下角提示显示 无法识别的USB设备 xff0c 我开始百度 xff08 还不会搭梯子用google xff09 xff0c 搜索结果大多说是要更新驱动 xff0c
  • 解决Android studio 模拟器闪烁黑屏问题

    首先 xff0c 必须感谢csdn大神给我的启示 xff0c 但是原文并没有解决我的问题 我在看 第一行代码 的时候 xff0c 跟着郭霖大神的思路 xff0c 想利用cmd命令查看虚拟机中的 db文件中的数据表 因为真机需要root才能查
  • Android studio如何更改应用程序的图标以及名称

    如何在Android studio中更改应用程序的图标和名称是很多初学者遇到的问题之一 xff0c 今天我就来给大家讲一下简单的步骤 1 更改图标 首先选中我们需要更改的工程 xff0c 然后new gt Image Asset 就来到了更
  • Matlab中矩阵的合并、某行某列的删除、矩阵大小的改变(完整的函数调用表)、矩阵元素的访问

    矩阵的合并 矩阵的合并就是把两个或两个以上的矩阵合并成一个新的矩阵 可用于构造矩阵 xff0c 也可用于合并矩阵 c 61 A B 就是在水平方向上合并矩阵A和矩阵B c 61 A B 就是在竖直方向上合并矩阵A和矩阵B 如下 xff1a
  • Matlab里for循环详解

    for循环用来重复指定次数 xff0c 由于for 循环变量 end组成 例1 xff1a span class token keyword for span i span class token operator 61 span span
  • 定时关机

    include 34 stdafx h 34 include lt windows h gt include lt windowsx h gt include lt shellapi h gt include 34 resource h 3
  • Android设置Settings:PreferenceFragment【4】

    Android设置Settings xff1a PreferenceFragment 4 最新的android谷歌官方设计文档指出 xff0c 在后续的Android开发中 xff0c 应尽量使用PreferenceFragment而不是P
  • Centos ansible部署,启动服务失败

    出现错误 xff1a Unable to enable service nginx Failed to execute operation Cannot send after transport endpoint shutdown解决方法
  • 抢红包算法--四种抢红包算法对比(附源码)

    还记着longlong ago 我还在做绿色征途手游版的时候 有天 策划同学要求同事 一定要优化下抢红包算法 本着划水第一 吃瓜并列第一的原则 于是 我听到了一堆数学名词 定理 XXX公式 呼 是我不配了 怪我没有好好学习 再仔细听一听 问
  • 抢红包算法--四种抢红包算法对比

    线上测试服务器中 xff0c 有个同事做的抢红包算法被要求优化 xff0c 大概听了下他们的讨论 xff0c 最后的结果竟然要用什么概率论等等一系列我听过的 没听过的名词去解决 我表示一脸懵 其实解决的问题就是一个 xff1a 抢红包算法不