游戏算法专题之PRD算法:听说你想凭运气抽中荣耀水晶?

news2024/12/26 23:04:34

PRD

PRD算法全称Pseudo-Random Distribution。是概率分布中的一种常见算法,在游戏开发领域中很常用。

PRD用于控制随机事件的触发概率,使其表现得更加符合预期,相比于传统得随机数生成,PRD算法可以平滑得控制随机事件的触发次数,确保不会出现过于极端的情况,比如长时间未触发或者频繁触发。

如果你觉得它离你很远,那么举个栗子,玩“王者荣耀”的多少都知道其中有一个道具叫荣耀水晶,这玩意就是靠抽奖的方式获得的,而根据官方抽奖的方式与概率公布等信息来看,抽取荣耀水晶的算法大概率也是PRD或者它的变种算法。

在游戏中,PRD算法的主要应用场景包括掉落系统、抽奖系统等。这些场景下,开发者通常希望在随机事件的发生上保持某种平衡,而PRD算法正是为了解决这一问题。


基本原理

PRD算法通过调整事件发生的概率来实现分布平滑。简单来说,它会随着事件未触发的次数增加,动态提升触发的概率,直到事件发生。一旦事件触发,概率会重置为初始值,重新开始计算。

PRD的公式通常为:$ P(n) = \frac{1}{k-f(n)}$

其中,P(n) 是事件在第 n 次尝试时的概率,k 是一个常数(通常设为 1),f(n) 是一个随尝试次数递增的函数,用于控制概率的增长。这个公式的作用是保证随着尝试次数的增加,概率不断增大,直至事件发生。


基本实现

下面就以使用PRD算法模拟抽取荣耀水晶的方式带着各位实现一下PRD算法吧!

  • 初始概率与递增概率:使用PRD算法,逐步提高未中奖后的中奖概率。

  • 保底次数:设置一个保底的次数上限,在达到该上限时,无论当前概率如何,玩家都会中奖。

  • 中奖后重置:当玩家中奖后,概率重置为初始值,并重新开始计算。


#include<iostream>
#include<cstdlib>
#include<ctime>
class PRDWithPity
{
private:
    double initialProb; // 初始中奖概率
    double increment; // 每次中奖时增加的概率
    double currentProb; // 当前中奖概率
    int pityLimit; // 保底次数
    int currentTry; // 当前抽奖次数

public:
    // 构造函数、初始化初始概率、递增概率和保底次数
    PRDWithPity (double initProb = 0.05,double inc = 0.02,int pity = 10) :
    initialProb(initProb),increment(inc),currentProb(initProb),pityLimit(pity),currentTry(0) {}

    bool draw()
    {
        currentTry++;

        // 达到保底,直接中奖
        if (currentTry >= pityLimit)
        {
            reset();
            return true;
        }

        // 生成0-1随机数
        double randNum = static_cast<double>(rand()) / RAND_MAX;

        // 如果随机数小于当前概率、表示中奖
        if (randNum < currentProb)
        {
            reset();
            return true;
        }else
        {
           currentProb += increment;
            return false;
        }
    }

    // reset函数
    void reset()
    {
        currentProb = initialProb;
        currentTry = 0;
    }
};

实现代码大致如上,没什么复杂的逻辑,关键地方也都添加了注释,这里就不再赘述了。

下面模拟测试一下看看效果

int main()
{
    srand(static_cast<unsigned>(time(0)));  // 初始化随机数种子
    PRDWithPity prd(0.000001, 0.0000002, 360);  // 初始中奖概率0.05,每次未中奖增加0.02,保底次数10次
    for (int i = 1; i <= 365; ++i) {
        if (prd.draw()) {
            std::cout << "恭喜屏幕前这位大佬第 " << i << " 次抽中一颗[荣耀水晶]" << std::endl;
        } else {
            std::cout << "第 " << i << " 次抽奖未中奖,幸运值+1,幸运值为: "
                      << prd.getCurrentTry()
                      << ",幸运值达到360必中一颗[荣耀水晶]。" << std::endl;
        }
    }
    return 0;
}

由于我们必须保证在触发保底之前中奖的概率足够低,因此这里直接将初始中奖率设置为0.000001也就是十万分之一,每次抽奖后递增中奖率也不能过高,比如可以设置在0.0000002(百万分之一)。这样可以保证在触发保底之前你大概率是不会抽到 荣耀水晶的,只能通过氪金不断的获取抽奖机会,直到抽够360次触发保底。

下面是本次抽奖的模拟结果:

第 1 次抽奖未中奖,幸运值+1,幸运值为: 1,幸运值达到360必中一颗[荣耀水晶]。
第 2 次抽奖未中奖,幸运值+1,幸运值为: 2,幸运值达到360必中一颗[荣耀水晶]。
第 3 次抽奖未中奖,幸运值+1,幸运值为: 3,幸运值达到360必中一颗[荣耀水晶]。
第 4 次抽奖未中奖,幸运值+1,幸运值为: 4,幸运值达到360必中一颗[荣耀水晶]。
第 5 次抽奖未中奖,幸运值+1,幸运值为: 5,幸运值达到360必中一颗[荣耀水晶]。
第 6 次抽奖未中奖,幸运值+1,幸运值为: 6,幸运值达到360必中一颗[荣耀水晶]。
第 7 次抽奖未中奖,幸运值+1,幸运值为: 7,幸运值达到360必中一颗[荣耀水晶]。
第 8 次抽奖未中奖,幸运值+1,幸运值为: 8,幸运值达到360必中一颗[荣耀水晶]。
第 9 次抽奖未中奖,幸运值+1,幸运值为: 9,幸运值达到360必中一颗[荣耀水晶]。
第 10 次抽奖未中奖,幸运值+1,幸运值为: 10,幸运值达到360必中一颗[荣耀水晶]。
第 11 次抽奖未中奖,幸运值+1,幸运值为: 11,幸运值达到360必中一颗[荣耀水晶]。
第 12 次抽奖未中奖,幸运值+1,幸运值为: 12,幸运值达到360必中一颗[荣耀水晶]。
第 13 次抽奖未中奖,幸运值+1,幸运值为: 13,幸运值达到360必中一颗[荣耀水晶]。
第 14 次抽奖未中奖,幸运值+1,幸运值为: 14,幸运值达到360必中一颗[荣耀水晶]。

[次数省略好多行]…

第 348 次抽奖未中奖,幸运值+1,幸运值为: 348,幸运值达到360必中一颗[荣耀水晶]。
第 349 次抽奖未中奖,幸运值+1,幸运值为: 349,幸运值达到360必中一颗[荣耀水晶]。
第 350 次抽奖未中奖,幸运值+1,幸运值为: 350,幸运值达到360必中一颗[荣耀水晶]。
第 351 次抽奖未中奖,幸运值+1,幸运值为: 351,幸运值达到360必中一颗[荣耀水晶]。
第 352 次抽奖未中奖,幸运值+1,幸运值为: 352,幸运值达到360必中一颗[荣耀水晶]。
第 353 次抽奖未中奖,幸运值+1,幸运值为: 353,幸运值达到360必中一颗[荣耀水晶]。
第 354 次抽奖未中奖,幸运值+1,幸运值为: 354,幸运值达到360必中一颗[荣耀水晶]。
第 355 次抽奖未中奖,幸运值+1,幸运值为: 355,幸运值达到360必中一颗[荣耀水晶]。
第 356 次抽奖未中奖,幸运值+1,幸运值为: 356,幸运值达到360必中一颗[荣耀水晶]。
第 357 次抽奖未中奖,幸运值+1,幸运值为: 357,幸运值达到360必中一颗[荣耀水晶]。
第 358 次抽奖未中奖,幸运值+1,幸运值为: 358,幸运值达到360必中一颗[荣耀水晶]。
第 359 次抽奖未中奖,幸运值+1,幸运值为: 359,幸运值达到360必中一颗[荣耀水晶]。
恭喜屏幕前这位大佬第 360 次抽中一颗[荣耀水晶]
第 361 次抽奖未中奖,幸运值+1,幸运值为: 1,幸运值达到360必中一颗[荣耀水晶]。
第 362 次抽奖未中奖,幸运值+1,幸运值为: 2,幸运值达到360必中一颗[荣耀水晶]。
第 363 次抽奖未中奖,幸运值+1,幸运值为: 3,幸运值达到360必中一颗[荣耀水晶]。
第 364 次抽奖未中奖,幸运值+1,幸运值为: 4,幸运值达到360必中一颗[荣耀水晶]。
第 365 次抽奖未中奖,幸运值+1,幸运值为: 5,幸运值达到360必中一颗[荣耀水晶]。


PRD的优点

平衡性PRD通过调整概率,使得随机事件更加平衡。例如,在掉落系统中,PRD确保物品不会长时间不掉落,也不会短时间内频繁掉落。

易于控制:开发者可以通过调节初始概率或递增函数的参数,来控制事件的发生频率和分布特性。

提升用户体验PRD可以防止用户在面对纯粹的随机系统时感到挫败,尤其是游戏中的奖励机制,通过PRD可以避免极端运气差的情况。

当然了,算法并非一成不变的,具体实现还得基于我们在开发业务中的具体需求来决定是否对原算法进行扩展、优化、变种。

比如下面这些扩展建议:

  1. 递增机制:可以根据具体需求将递增值设计为动态调整,而不仅仅是固定值。
  2. 外部配置:如果PRD用于实际的游戏开发中,概率和递增值通常从外部配置表中读取,而不是硬编码在程序中。

小结

如果你不从事游戏开发相关领域工作,那么这篇文章可以帮你了解身边游戏抽奖的中奖机制和原理,在面对华丽的游戏虚拟道具抽奖时,请务必保持理性消费(有钱人忽略,因为本质就是来圈你们这些所谓有钱人的RMB滴!)代码面前,不要对自己的运气抱有过高的自信!

如果你是一个即将或者是正在从事游戏开发工作,那么学无止境,共勉!

这是一个连载的专题,欢迎持续关注哦!

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

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

相关文章

cJSON-轻量级解析模块、字符串的神——编织STM32C8T6与阿里云信息传递的纽带

编写方向&#xff1a;本人就不泛泛的编写一篇什么一文学会cJSON了&#xff0c;没什么突出点&#xff0c;也就我水水字数&#xff0c;你们看来看去也不懂&#xff0c;本人是从上阿里云传信息接触的cJSON的&#xff0c;我就此写一篇针对性的文章&#xff0c;希望对大家有用&#…

通信工程学习:什么是UNI用户网络接口

UNI&#xff1a;用户网络接口 UNI&#xff08;User Network Interface&#xff09;用户网络接口&#xff0c;是网络通信中的一个重要概念&#xff0c;它连接了用户设备与智能光网络或其他类型的网络。以下是关于UNI用户网络接口的详细解释&#xff1a; 一、定义与功能 定义&am…

VSCode C++(Code Runner)+ OpenSSL开发环境搭建

本章教程,主要介绍在VSCode中配置OpenSSL环境。 一、安装 OpenSSL 首先,我们需要安装OpenSSL,并配置OpenSSL系统环境变量。 1、下载OpenSSL 下载地址:https://slproweb.com/products/Win32OpenSSL.html 如果下载慢可以通过下方网盘进行下载: 通过网盘分享的文件:Win64Op…

Geneformer AI 模型,有限数据也能解锁基因网络

目录 类似于 BERT 的单单元数据参考模型 NVIDIA Clara 工具组合用于药物研发 用于疾病建模的基础 AI 模型 Geneformer 是最近推出的 和功能强大的 AI 模型&#xff0c;可以通过从大量单细胞转录组数据中进行迁移学习来学习基因网络动力学和相互作用。借助此工具&#xff0c;…

ICPC网络赛 以及ACM训练总结

一、训练反思 关于我自己暑假期间训练的反思&#xff0c;我承认无论是因为什么原因&#xff0c;我自己浪费我整整一个暑假的时间&#xff0c;暑假期间正是我们集训的关键时期&#xff0c;这期间没有任何的事情来打扰我们学习&#xff0c;而我却熬夜&#xff0c;白天训练懈怠&a…

C++类与对象(二)超详细

目录 1.类的6个默认成员函数 2..构造函数 2.1概念 2.2 特征 3.析构函数 3.1 概念 3.2 特性 4.拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载函数 5.1 运算符重载&#xff08;是否重载这个运算符是看这个运算符对这个类是否有意义&#xff09; 5.2 赋值运算符重…

嵌入式单片机程序运行基本机理

1. 程序各种要素说明 大家好,今天用一个最简单的程序跟大家讲清楚程序的构成。 1.1. 概述 硬件首先要知道硬件的组成。 在前面章节我们说过,芯片包含Flash和RAM。 他们虽然不是相同的东西,但是都属于同一个地址空间,32位芯片的地址空间大小是4G。 比如ST32,FLASH通常从…

在 FlexSim 中使用 OpenUSD 分析、可视化和优化现实世界的流程

对于制造和工业企业而言&#xff0c;效率和精度至关重要。为了简化运营、降低成本和提高生产力&#xff0c;各公司正在转向数字孪生和离散事件模拟。 离散事件模拟使制造商能够通过试验不同的输入和行为来优化流程&#xff0c;这些输入和行为可以逐步进行建模和测试。 FlexSi…

基于Python实现的一个电影知识库QA系统

1. 实现效果 1. 图形展示 这是使用echarts.js 来实现的自定义页面的图谱展示&#xff0c;当然还有其他的库也能实现类似的效果&#xff0c;这里看各位的选择。 这里我在每个实体之间都实现了双层关系的绑定&#xff0c;这对于后面实现检索会有点帮助 2. 实体搜索展示 这里…

中断门+陷阱门

中断门&#xff1a; 中断描述符在IDT表里面 kd> dq idtr 80b95400 83e48e000008bfc0 83e48e000008c150 80b95410 0000850000580000 83e4ee000008c5c0 80b95420 83e4ee000008c748 83e48e000008c8a8 80b95430 83e48e000008ca1c 83e48e000008d018 80b95440 000085000050…

回溯-重新安排行程

1.排序 Collections.sort(list,(o1, o2)-> o1.get(0).compareTo(o2.get(0))); 2.返回值 3.往集合添加元素 Arrays.asList(元素) List<List<String>> list new ArrayList<>();List<String> path new ArrayList<>();// 将[["JFK"…

沉浸式体验和评测Meta最新超级大语言模型405B

2024年7月23日&#xff0c; 亚马逊云科技的AI模型托管平台Amazon Bedrock正式上线了Meta推出的超级参数量大语言模型 - Llama 3.1模型&#xff0c;小李哥也迫不及待去体验和试用了该模型&#xff0c;那这么多参数量的AI模型究竟强在哪里呢&#xff1f;Llama 3.1模型是Meta&…

idea激活页面怎么打开

打开Help------选择Register 然后就可以选择激活方式了

Vue2学习笔记(01计算属性和监视属性)

1、事件修饰符 2、计算属性-computed 要显示的数据不存在&#xff0c;要通过计算得来。在computed对象中定义计算属性。在页面中使用{{方法名}}来显示计算的结果。 3、监视属性-watch 通过vm对象的$watch()或watch配置来监视指定的属性当属性变化时,回调函数自动调用,在函数内…

Games101图形学笔记——光栅化

这里写目录标题 Rasterization光栅化屏幕空间隔行扫描三角形采样采样产生的问题反走样处理方法&#xff1a;采样前模糊 频率&#xff0c;时域傅里叶级数展开傅里叶变换 滤波高通滤波低通滤波 卷积卷积的一些定理 反走样MSAA&#xff08;Multisample Anti-Aliasing&#xff09;多…

C++_20_多态

多继承会造成 菱形继承** 使用虚继承来解决 不是给爷爷类加 也不是给子类加 是给父类加 虚基指针和虚基表 多态 概念&#xff1a; 概念&#xff1a; 一个事物的多种形态&#xff0c;简称多态 如&#xff1a; 对象的多态 ​ 张三 ​ 在对象面前 怂 ​ 在朋友面前 谄媚 ​ 在父…

Axure科技感大屏系统设计:智慧农场管理平台

在数字化转型的浪潮中&#xff0c;数据可视化作为连接现实世界与数字世界的桥梁&#xff0c;正以前所未有的速度改变着各行各业的面貌。智慧农业作为现代农业的重要发展方向&#xff0c;其管理平台的数据大屏设计尤为重要&#xff0c;它不仅是农场运营状况的直接展示窗口&#…

3. Python计算水仙花数

Python计算水仙花数 一、什么是水仙花数&#xff1f; 百度答案 二、怎样使用Python计算水仙花数&#xff1f; 这里需要for循环&#xff0c;if判断&#xff0c;需要range()函数&#xff0c;需要知道怎么求个位数&#xff0c;十位数&#xff0c;百位数… 1. For循环 语句结…

【Android Studio】API 29(即Android 10)或更高版本,在程序启动时检查相机权限,并在未获取该权限时请求它

文章目录 1. 在AndroidManifest.xml文件中&#xff0c;声明相机权限&#xff1a;2. 在你的Activity中&#xff08;例如MainActivity&#xff09;测试 1. 在AndroidManifest.xml文件中&#xff0c;声明相机权限&#xff1a; <uses-feature android:name"android.hardwar…

OS:初识操作系统——邂逅与启航

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;实践是检验真理的唯一标准&#xff01;&#xff01;&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 前言 各位uu好&#xff0c;现在我们要开始一个新的篇章——操作…