PRD 알고리즘 소개
PRD 의사 난수 알고리즘은 게임에서 치명타 알고리즘으로 자주 사용됩니다. 이 알고리즘은 치명타 발생을 전적으로 운에 의존하지도 않고 정확하게 예측할 수도 없게 하며 둘 사이의 절충안입니다.
PRD 알고리즘의 표현은 매우 간단합니다.
P(N) = C * N
N은 현재 공격 횟수, P(N)은 현재 공격의 치명타율, C는 확률 증가분을 나타낸다. 공격이 치명타로 이어지면 N을 1로 재설정해야 합니다. 이 공격이 치명타로 이어지지 않으면 N + 1입니다.
C는 실제 확률 P를 통해 직접 구할 수 있는데, 즉 C와 실제 확률 사이에는 일대일 대응이 존재한다.예를 들어 실제 임계 확률 P가 0.5라면 C는 항상 0.3이다.
구체적인 알고리즘은 다음 링크에 자세히 소개되어 있습니다. PRD crit 알고리즘, 게임에서 흔히 사용되는 의사 랜덤 알고리즘
C#은 PRD 알고리즘을 구현합니다.
환경: Visual Studio를 사용하여 콘솔 애플리케이션을 생성합니다.
코드는 다음과 같습니다.
class PRD
{
//总概率
public double p;
//概率增量
public double c;
// 根据 传入 C 值,计算该C值下,最小暴击范围的平均暴击率
private double PFromC(double c)
{
double dCurP = 0d;
double dPreSuccessP = 0d;
double dPE = 0;
int nMaxFail = (int)Math.Ceiling(1d / c);
for (int i = 1; i <= nMaxFail; ++i)
{
dCurP = Math.Min(1d, i * c) * (1 - dPreSuccessP);
dPreSuccessP += dCurP;
dPE += i * dCurP;
}
return 1d / dPE;
}
// 根据实际暴击概率P,计算概率增量C
public double CFromP(double p)
{
double dUp = p;
double dLow = 0d;
double dMid = p;
double dPLast = 1d;
while (true)
{
dMid = (dUp + dLow) / 2d;
double dPtested = PFromC(dMid);
if (Math.Abs(dPtested - dPLast) <= 0.00005d) break;
if (dPtested > p) dUp = dMid;
else dLow = dMid;
dPLast = dPtested;
}
return dMid;
}
//当前攻击的次数
private int attackCount = 1;
//返回是否暴击,如果没暴击则攻击次数加1,如果暴击了则攻击次数重置为1
public bool IsCriticalHit()
{
if (new Random().Next(1, 101) <= (int)(c * 100d) * attackCount)
{
attackCount = 1;
return true;
}
else
{
attackCount++;
return false;
}
}
}
class Program
{
static void Main(string[] args)
{
PRD prd = new PRD();
//设置暴击率为0.5
prd.p = 0.5;
Console.WriteLine("暴击率为" + (int)(prd.p * 100d) + "%");
//计算概率增量
prd.c = prd.CFromP(prd.p);
//按n键模拟攻击一次
int iCriticalHitCount = 0, iTotolCount = 0;
while (Console.ReadKey().Key == ConsoleKey.N)//检测到玩家按N键
{
if (prd.IsCriticalHit())
{
iCriticalHitCount++;
Console.WriteLine("暴击!!!!!!!!");
}
else
{
Console.WriteLine("未暴击");
}
iTotolCount++;
Console.WriteLine("总次数:" + iTotolCount + ",暴击次数" + iCriticalHitCount +
",总暴击率:" + (int)((double)iCriticalHitCount / (double)iTotolCount * 100d) + "%");
Console.WriteLine("----------------------------");
}
}
}