问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

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

创作时间:
作者:
@小白创作中心

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

引用
CSDN
1.
https://blog.csdn.net/2302_76401343/article/details/142290044

PRD(Pseudo-Random Distribution)算法是一种在游戏开发领域中常用的概率分布算法,主要用于控制随机事件的触发概率,使其表现得更加符合预期。本文将详细介绍PRD算法的基本原理、应用场景以及具体实现方法,并通过C++代码示例和模拟测试结果进行说明。

基本原理

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滴!)代码面前,不要对自己的运气抱有过高的自信!

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

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号