概率和随机是紧密相关的概念,它们常用于决定事件的发生与结果;在大多数游戏中,概率和随机数的使用涉及到随机事件的模拟,例如:
- 伤害计算
- 随机事件发生
- 物品掉落
- Rouge Like
- 抽牌,掷骰子
- AI决策和行为树
- Slots
- 装备合成或转化
- Mystery Box Like
- 物理模拟和碰撞检测
它们的发生的概率是相等或不等的,数值是随机的或伪随机的
随机和概率在非强竞技场景下的应用非常广泛,或增加了游戏性、或提高了游戏性能、或旨在提升玩家游戏时长、或旨在增加游戏营收,由此产生的效益和收益无疑是巨大的
从游戏实现到数值设计,从具体实现到哲学抽象
概率与随机的定义
概率是一种数学概念,指的是某件事情发生的可能性,通常用0到1之间的数字表示
- P = 0:事件不可能发生
- P = 1:事件一定会发生
- P = 0.5:事件发生和不发生的概率相同
随机是指一种无法确定的行为或过程,结果是不可预测的,但概率可以帮助对其结果进行量化和描述
具体实现
游戏中概率的实现方式通常取决于具体需求,以下是常见的几种实现
随机数生成器(Random Number Generator, RNG)
这是最常见的做法,通过随机数来模拟概率事件,可以用来生成各种各样的结果,如掉落物品、敌人AI行为、事件触发等
均匀分布(Uniform Distribution) 对于简单的概率,可以通过生成一个范围内的随机数,判断其是否满足概率条件 例如,假设在0~1之间生成一个随机数
r,并且下那个要这个事件发生的概率为P,则float r = Random.Range(0f, 1f); float P = 0.7f; if (r <= P) { // 事件发生 }这样,当
r小于P时,事件就会发生,发生的概率是P正态分布(Normal Distribution) 如果需要模拟像物理系统、敌人行为等更复杂的概率,可以使用更复杂的分布来模拟
比如正态分布可以用来模拟某些变量在某个中心值附近波动的情况,例如敌人的伤害、成绩、健康指标
float mean = 50f; // 均值 float stdDev = 10f; // 标准差 float randomValue = Mathf.Clamp(RandomNormal(mean, stdDev), 0f, 100f); // 生成随机值,区间[0, 100],Mathf.Clamp为区间限制 float RandomNormal(float mean, float stdDev) { float u1 = Random.Range(0f, 1f); float u2 = Random.Range(0f, 1f); float z0 = Mathf.Sqrt(-2f * Mathf.Log(u1)) * Mathf.Cos(2f * Mathf.PI * u2); // Box-Muller变换:这是一种通过两个均匀分布的随机数来生成标准正态分布(均值为0, 标准差为1)的随机数方法 return mean + z0 * stdDev; // 将z0转换为具有给定均值和标准差的正态分布随机数 }Box-Muller变换公式:$z_0=\sqrt{-2\cdot\ln(u_1)}\cdot\cos(2\cdot\pi\cdot u_2)$
泊松分布(Possion Distribution) 泊松分布是一个用于描述单位时间或单位区域内随机事件发生次数的概率分布,特别适用于那些事件独立且发生频率较低的场合
泊松分布概率质量函数(PMF):$P(k,\lambda)=\frac{\lambda^k\cdot e^{-\lambda}}{k!}$
其中:
- k是事件发生的次数(非负整数)
- λ是单位时间或单位区域内的平均事件发生次数(称为泊松分布的参数)
- e是自然对数底数
泊松分布常用于模拟稀有事件,比如在一个时间段内生成一个非常小概率的事件(例如,掉落西游物品)。这种情况比较少见,但在模拟游戏中的“稀有事件”时很有用
应用场景:生成爆发性事件,比如在某个特定条件下掉落非常稀有的物品
泊松分布的随机数生成
为了生成符合泊松分布的随机数,可以使用逆变换抽样法
- 首先生成一个均匀分布的随机数u∈[0,1)
- 然后通过求和方式生成符合泊松分布的随机数
// 生成符合泊松分布的随机数
int GeneratePoissonRandom(double lambda)
{
// 使用逆变换抽样法来生成泊松分布的随机数
int k = 0;
double L = Math.Exp(-lambda); // e^(-λ)
double p = 1.0; // 初始值
double sum = 0.0;
// 循环直到累积的概率小于L
while(p > L)
{
++k;
p *= Random.Shared.NextDouble(); // 生成[0, 1)之间的随机数
sum += p;
}
return k - 1; // 返回事件发生的次数
}
解释:
- 生成泊松分布的随机数:
lambda是泊松分布的参数,表示单位时间或单位区域内平均时间发生次数- 通过计算 $e^{-λ}$ 得到
L,然后生成一个随机数p,并将它与L进行比较 - 在
p大于L的情况下,继续累积随机数并增加事件发生次数k。直到p小于L,返回事件发生次数
- 逆变换法:
- 通过不断生成随机数并进行乘积,直到满足某些条件来生成符合泊松分布的随机数,这个过程通常使用累积概率的方式来进行
加权概率
如果想给不同的事件分配不同的发生概率,可以使用加权概率
这常用于掉落系统、选择不同的路径、敌人AI决策等
举例:
假设有四个掉落物品和它们掉落的概率分别是:
- A:50%
- B:30%
- C:15%
- D:5%
可以将这些概率转化为权重
int[] weights = {50, 30, 15, 5}; // 权重数组
int totalWeight = weights.Sum(); // 权重总和
int randomValue = Random.Range(0, totalWeight);
int chosenItemIndex = 0;
int weightSum = 0;
// 根据随机值选择物品
for (int i = 0; i < weights.Length; ++i)
{
weightSum += weights[i];
if (randomValue < weightSum)
{
chosenItemIndex = i;
break;
}
}
这样,便可以根据权重控制概率的分布
事件驱动(状态机)
在一些复杂的游戏中,可能不仅仅是通过概率来决定事件的发生,还需要多重条件和事件的交互。
例如,AI行为可能需要根据玩家的行为,敌人血量等多重因素来随机选择。
可以使用类似状态机的结构,根据多个条件来触发不同的概率事件。
表格查找
通过查找一个概率表或曲线来返回结果,这在特定场景下非常有用
例如玩家经验值的增长可以通过查表来确定每个经验值段的增长概率
Dictionary<int, float> experienceTable = new Dictionary<int, float>
{
{1, 0.2f},
{2, 0.5f},
{3, 0.3f},
};
int level = 2;
if (Random.Range(0f, 1f) < experienceTable[level])
{
// 根据经验表,玩家升到下一等级
}
伪随机与真随机
游戏中通常使用的是伪随机数生成器(PRNG),它通过算法生成看似随机的数字,但实际上是可能预测的(如果知道种子)。对于一些安全性要求较高的情况,可能会使用真随机数生成器(例如利用系统时间、硬件噪声等)
伪随机数(Pseudo-Random Number)
伪随机数是通过某种算法从一个初始值(称为种子)生成的看似随机的数。虽然生成的数值分布和真正的随机数非常相似,但它们是可预测的,只要知道了生成过程的算法和种子值,理论上就可以预测后续的随机数
生成方式
伪随机数生成器(PRNG)通常依赖于一个数学公式(或递归关系)来计算每个随机数。最常见的PRNG算法是线性同余法(Linear Congruential Generator, LCG) 和 梅森旋转算法(Mersenne Twister)
例如,梅森旋转算法(Mersenne Twister)是一个非常流行的伪随机数生成器,它提供了均匀分布的高质量随机数,并且有非常长的周期(大约2^32 - 1次生成)
特点
- 确定性:伪随机数的生成是基于种子值的,因此它是可预测的,只要知道种子和算法,就能预测所有生成的数值
- 高效:伪随机数生成器的计算过程是非常高效的,通常不需要额外的硬件支持
- 周期性:伪随机数有一个固定的周期,过了这个周期,它会重复
应用
- 游戏开发:伪随机数用于游戏中的随机事件生成(如物品掉落、战斗伤害等)
- 模拟与建模:如蒙特卡洛模拟、物理模拟等
- 加密:虽然伪随机数是可以预测的,但在某些加密算法中,伪随机数可以用于生成密钥等,前提是它们要足够难以预测
// 使用种子初始化伪随机数生成器
System.Random rng = new System.Random(12345); // 种子为12345
int randomNumber = rng.Next();
真随机数(True Random Numbers)
真随机数是通过物理过程产生的随机数,而非通过算法计算。它们的生成依赖于某种物理现象(例如电子噪声、放射性衰变等),这些现象的结果是完全不可预测的,具有完全的随机性
生成方式
真随机数通常是由硬件随机数生成器(HRNG)生成。这些生成器通过测量物理现象(如热噪声、光子的运动等)来产生随机数。常见的硬件随机数生成器有基于噪声的电路、光子计数器等。
例如,Intel的RDRAND指令可以通过硬件生成真随机数,或者一些第三方硬件设备也可以用来生成真随机数
特点
- 完全不可预测
- 慢速
- 无周期性
应用
- 加密:在安全性要求较高的应用中,真随机数被用来生成加密密钥、初始化向量(IV)等,确保密码学算法的安全性
- 科学实验:如量子随机数生成等领域,真随机数可用于保证实验的完全随机性
- 高度安全应用:如数字签名、身份验证和区块链技术中的随机数
有些平台提供硬件支持来生成真随机数,例如Intel的RDRAND指令,或者使用操作系统提供的随机源(如/dev/random)
// C语言示例:从/dev/random读取真随机数
FILE* fp = fopen("/dev/random", "r");
unsigned int rand_num;
fread(&rand_num, sizeof(rand_num), 1, fp);
fclose(fp);
蒙特卡洛方法模拟连续概率事件
有些游戏可能需要连续的概率事件,而不仅仅是一次性判定。比如,在模拟物理系统、模拟经济系统等复杂系统中,连续的事件可能会影响最终的概率结果
蒙特卡洛方法(Monte Carlo Method)是一类基于随机数的数值计算方法,通过模拟随机样本来解决数学问题,特别是那些难以通过传统解析方法解决的问题。它得名于摩纳哥的蒙特卡洛赌场,因为这种方法的核心思想与赌博中的随机性相似——依赖于大量的随机抽样来推测最终结果。
基本概念
蒙特卡洛方法通过生成大量的随机数来模拟系统的行为,并通过这些随机样本来估计一个复杂系统的结果或计算一个数学问题的近似解。它尤其适用于求解那些具有复杂概率模型或多维积分的问题。
应用场景
蒙特卡洛方法可以应用于很多领域,特别是那些无法通过简单解析解法解决的问题。常见的应用包括:
- 数值积分:特别是高维积分
- 优化问题:例如最小化或最大化某个目标函数
- 物理模拟:如粒子模拟、统计物理
- 金融工程:如期权定价、风险分析
- 机器学习:如贝叶斯推理、Markov Chain Monte Carlo (MCMC) 方法
蒙特卡洛方法的基本步骤
- 定义问题:首先明确想要解决的问题,通常是计算某个期望值、概率、积分等
- 随机采样:根据问题的定义,随机生成一组输入样本。这些样本可以来自一个已知的概率分布或均匀分布
- 评估样本:对每个样本计算相应的函数值(或其他相关量)
- 求取统计量:对所有样本的结果进行统计分析,通常是求取平均值、方差等统计量
- 结果估计:根据样本的统计量来估计问题的解
应用实例
示例1:估计圆周率(π)
最经典的蒙特卡洛方法之一是通过随机点来估计圆周率(π)。可以将一个单位正方形放入单位圆内,然后随机生成一些点,计算这些点落在圆内的比例。
步骤:
- 随机生成一些点
(x, y),其中x和y的取值范围为[0, 1] - 判断每个点是否落在单位圆内:即检查
x^2 + y^2 <= 1 - 计算落在圆内的点的比例,乘以4就是π的近似值
数学推导:
单位元的面积是π * r ^ 2,而正方形的面积是1 * 1 = 1,所以落在圆内的点的比例大约是π / 4
// C#
int totalPoints = 1000000;
int pointsInsideCircle = 0;
for (int i = 0; i < totalPoints; ++i)
{
float x = Random.Range(0f, 1f);
float y = Random.Range(0f, 1f);
if (x * x + y * y <= 1) ++ pointsInsideCircle;
float piEstimate = 4 * (float)pointsInsideCircle / totalPoints;
}
示例2:估算积分
估算函数f(x) = x ^ 2在区间[0, 1]上的定积分
步骤:
- 随机生成
N个x值,范围是[0, 1] - 对于每个
x,计算f(x) = x ^ 2的值 - 求所有
f(x)的平均值,并乘以区间长度
数学推导:
积分的近似值就是f(x)的平均值乘以区间长度
// C#
int totalSamples = 1000000;
float sum = 0;
for (int i = 0; i < totalSample; ++i)
{
float x = Random.Range(0f, 1f);
sum += x * x;
}
float integralEstimate = sum / totalSamples;
蒙特卡洛方法的优缺点
优点:
- 适用性广泛:可以处理各种复杂的、高维度的、概率性的数学问题,尤其是解析解难以获得时
- 简单易实现:不需要数学推导
- 并行计算:蒙特卡洛方法中的样本生成和评估通常是独立的,因此可以很容易地并行化,适合现代多核处理器
缺点:
- 精度较低:近似解,样本数量和计算成本成反比,和计算精度成正比
- 计算量大:尤其是在高维空间中,所需的样本数量增长非常快,导致计算量增加
- 误差评估:在使用蒙特卡洛方法时,通常需要进行误差分析,估算所得到的结果的误差范围
蒙特卡洛方法的改进
- 重要性抽样:在采样时,优先选择那些对结果贡献较大的区域,从而提高效率
- 方差降低技术:如控制变量法和分层抽样,通过减少方差来加速收敛
- MCMC:用于高维空间的样本生成和推理,常用于统计学和机器学习中
蒙特卡洛方法在游戏中的应用
哲学抽象
随机性与平衡性
游戏中的随机性往往需要与游戏的平衡性进行协调。过高的随机性可能导致游戏体验的不稳定和不可预测,而过低的随机性则可能让游戏失去挑战性和趣味性。例如,在战斗系统中,伤害值可能会受到随机性影响,但在确保平衡的前提下,可以给玩家一定的“可预测性”来增加策略性
常见随机性问题:
- 冰箱效应:如果某个事件的概率过高或过低,玩家可能会觉得过于频繁或完全不可能发生
- 冷却期:有时玩家可能觉得某些事件的触发过于随机,尤其是在有多个成功或失败条件的情况下。适当地调整“冷却期”或引入玩家操作的变量,能有效避免这种感觉
使用随机数种子
为了避免“随机性”被完全掌控(比如在测试时),开发者可以人为设定随机数的种子,通过固定种子来保证结果的可复现性
多人游戏中的随机性
在多人游戏中,同步随机性(例如,随机数的生成应该在所有玩家的客户端和服务器上同步)可能成为一个问题。为了保证公平性,通常会把随机数生成放在服务器端进行,并将结果同步到客户端
