当前位置:  开发笔记 > 编程语言 > 正文

需要可预测的随机发生器

如何解决《需要可预测的随机发生器》经验,为你挑选了16个好方法。

我是一名网络游戏开发者,我遇到了随机数问题.假设玩家有20%的几率用他的剑获得重击.这意味着,5次点击中的1次应该是至关重要的.问题是我的现实生活成绩非常糟糕 - 有时候球员在5次安打中得到3次降落,有时15次命中都没有.战斗相当短(3-10次命中),因此获得良好的随机分布非常重要.

目前我使用PHP mt_rand(),但我们只是将代码移动到C++,所以我想在游戏的新引擎中解决这个问题.

我不知道解决方案是否是一些统一的随机生成器,或者可能记住以前的随机状态以强制正确分配.



1> Esko Luontol..:

这意味着,5次点击中的1次应该是至关重要的.问题是我的现实生活成绩非常糟糕 - 有时候球员在5次安打中得到3次降落,有时15次命中都没有.

你需要的是一个洗牌袋.它解决了游戏中随机过于随机的问题.

该算法大致如下:您将1个关键点和4个非关键点击放入包中.然后你将他们的订单随机化,并一次挑出一个.当行李是空的时,再次使用相同的值填充它并随机化.通过这种方式,您每5次点击平均可获得1次重击,最多可连续获得2次关键命中和8次非关键命中.增加包中的商品数量以获得更多随机性.

下面是我之前写的一个实现(在Java中)及其测试用例的示例.


+ 1为好主意而不批评.缩小袋子以获得更高程度的随机性,并处理玩家之间的重要机会差异(如果变量为c)
你的袋子大小可以是10.一次打击,加上一次30%的几率.如果关键机会发生变化,你可以扔掉包并重新开始.请注意,任何此类方案都存在这样的风险:如果您(或您的对手)知道您的重击概率和行李的大小,您有时可以确定您不会获得一定数量的滚动的另一个关键.这可能会影响战术.
我喜欢洗牌包的想法,但我不认为这匹配游戏"精神",因为你的爆击概率为20%(这意味着你不能在10次点击中做任何事情)不再是一个概率.每5次点击就会命中1次.此外,如果重大命中概率变化将重新滚动行李将导致游戏中的故障.如果我的重击已经完成,我会自己施展咒语以获得下一个批评者:p
是的......在这样的事情中你运行类似于卡计数的ricks.我是冒一个风险还是进行大杀...一小组固定的潜在结果可以降低损失的风险并增加被"游戏"的机会
@Jonathan:让你的包大尺寸你给它实际上解开整个包的想法:确保在可接受的投掷量内发生的事情(实现的关键).使袋50000变大与使用随机数发生器大致相同.

2> ceejayoz..:

你对随机意味着什么有误解.

哪个更随机?

在此输入图像描述 在此输入图像描述

虽然第二个图看起来分布更均匀,但实际上第一个图更随机.人类的思维常常看到随机性的模式,所以我们将第一个图中的团块视为模式,但它们不是 - 它们只是随机选择的样本的一部分.


好Numb3rs解释!
从技术上讲,你无法衡量随机性.两个发行版对我来说都是相当随意的,尽管我的猜测都是通过算法生成的.您可以对第一个绘图执行多个测试,并确定它可能来自根据均匀分布放置点的过程,但您无法断定它更随机.作为一个反例,您可以使用线性同余发生器制作第一个图,然后使用来自齐纳二极管的放大噪声制作第二个图.尝试不相关的词而不是随机的.
您可以测量所述分布随机的概率.
@Dietich:[当然你可以测量随机性](http://en.wikipedia.org/wiki/Entropy_%28information_theory%29)
公平地说,虽然OP可能没有使用正确的术语,但他知道随机数生成器给他的东西更像第一个图,当他想要更像第二个图时,因为它*感觉*更"公平"用户.

3> AHelps..:

考虑到你要求的行为,我认为你正在随机化错误的变量.

而不是随机化命中是否至关重要,尝试随机化转数,直到发生下一次重击.例如,每次玩家获得一个关键时,只需在2和9之间选择一个数字,然后在经过多轮之后给他们下一个关键数字.您还可以使用骰子方法来接近正态分布 - 例如,您将在2D4轮次中获得下一个关键.

我相信这种技术也可用于在世界范围内随机遭遇的RPG中 - 你随机化一个步数计数器,然后经过很多步骤,你再次被击中.感觉更加公平,因为你几乎不会连续遭遇两次遭遇 - 如果这种情况发生一次,那么球员就会变得烦躁.


随机化转数是一个好主意!

4> Adam Wright..:

首先,定义"正确"的分配.随机数是随机的 - 您看到的结果与(伪)随机性完全一致.

在此基础上,我假设你想要的是一种"公平"的感觉,所以用户不能没有成功就转100圈.如果是这样,我将跟踪自上次成功以来的失败次数,并对生成的结果进行加权.让我们假设你想要五分之一的卷"成功".所以你随机生成一个从1到5的数字,如果它是5,那就太好了.

如果没有,记录失败,下次生成一个从1到5的数字,但加上说,floor(numFailures/2).所以这一次,他们又有五分之一的机会.如果失败,下次获胜间隔为4 5; 成功的机会是五分之二.有了这些选择,经过8次失败后,他们肯定会成功.


+1用于查看请求背后的问题而不是告诉OP他误解随机.
请注意,如果你这样做,那么它们的总体成功比例将大于1中的一个.一种方法是(例如)从1..100范围内随机选择20个不同的数字,并预先确定那些将是他们的批评.不过,这是一堆书记.

5> Ian Terrell..:

我同意早先的答案,即一些游戏的小规模运行中的真实随机性是不可取的 - 对某些用例来说似乎太不公平了.

我在Ruby中编写了一个简单的Shuffle Bag,并进行了一些测试.实施这样做:

如果它看起来仍然公平或者我们还没有达到最小滚动的阈值,它会根据正常概率返回一个公平的命中.

如果从过去的滚动中观察到的概率使其看起来不公平,则会返回"公平的"命中.

基于边界概率,这被认为是不公平的.例如,对于20%的概率,您可以将10%设置为下限,将40%设置为上限.

使用这些边界,我发现在10次点击运行中,14.2%的时间真正的伪随机实现产生的结果超出了这些界限.大约11%的时间,0次关键命中在10次尝试中得分.3.3%的时间,5个或更多的关键命中率从10个中降落.当然,使用这种算法(最小滚数为5),"Fairish"运行的数量少得多(0.03%)超出界限.即使下面的实现不合适(当然可以做更聪明的事情),值得注意的是,通常你的用户会觉得它对真正的伪随机解决方案是不公平的.

这是我FairishBag用Ruby编写的.此处提供了整个实施和快速蒙特卡罗模拟(要点).

def fire!
  hit = if @rolls >= @min_rolls && observed_probability > @unfair_high
    false
  elsif @rolls >= @min_rolls && observed_probability < @unfair_low
    true
  else
    rand <= @probability
  end
  @hits += 1 if hit
  @rolls += 1
  return hit
end

def observed_probability
  @hits.to_f / @rolls
end

更新:使用此方法确实可以使用上述边界将获得重击的总概率提高到约22%.您可以通过将其"实际"概率设置得稍低来抵消这一点.通过精细修改的概率为17.5%,观察到的长期概率约为20%,并使短期运行保持公平.



6> Colin Pickar..:

如何用这样的东西替换mt_rand()?

XKCD漫画(RFC 1149.5指定4作为标准的IEEE审查随机数.)

(RFC 1149.5将4指定为标准的IEEE审查随机数.)

来自XKCD.


这比OP想要的更随机
+1因为它带来了对随机性的洞察力.

7> CiscoIPPhone..:

希望本文能为您提供帮助:http://web.archive.org/web/20090103063439/http : //www.gamedev.net :80/reference/design/features/randomness /

这种生成"随机数"的方法在rpg/mmorpg游戏中很常见.

它解决的问题是这个(摘录):

刀片蜘蛛在你的喉咙.它打了你想念.它再次击中,你再次错过.一次又一次,直到你没有任何东西可以击中.你已经死了,还有一个两吨重的蜘蛛在你的尸体上幸灾乐祸.不可能?不可以吗?是.但是如果给予足够多的球员并给予足够的时间,那么不可能的几乎可以肯定.这不是刀片蜘蛛很难,这只是运气不好.多么令人沮丧.这足以让玩家想要退出.



8> Suma..:

你想要的不是随机数,而是人类看似随机的数字.其他人已经提出了个人算法,可以帮助你,比如Shuffle Bad.

有关此域的详细而广泛的分析,请参阅AI Game Programming Wisdom 2.对于任何游戏开发者来说,整本书值得一读,"貌似随机数"的概念在章节中处理:

AI决策和游戏逻辑的过滤随机性:

摘要:传统观点认为,随机数生成器越好,你的游戏就越难以预测.然而,根据心理学研究,短期内的真正随机性往往对人类来说显然是不随意的.本文展示了如何使随机AI决策和游戏逻辑看起来更随机,同时仍保持强大的统计随机性.

您可能还会发现另一个有趣的章节:

随机数统计

摘要:人工智能和游戏一般使用随机数最多.忽视他们的潜力是让游戏变得可预测和无聊.错误地使用它们可能就像完全忽略它们一样糟糕.了解随机数如何生成,它们的局限性和能力,可以消除在游戏中使用它们的许多困难.本文提供了对随机数,它们的生成以及将好的和坏的分开的方法的见解.



9> 小智..:

当然,任何随机数生成都有可能产生这样的运行?您不会在3-10卷中获得足够大的样本集来查看适当的百分比.

也许你想要的是一个怜悯的门槛......记住最后10卷,如果他们没有受到重击,给他们一个免费赠品.平滑随机的吊索和箭头.



10> Hank Gay..:

您最好的解决方案可能是使用多种不同的随机方案进行游戏测试,并选择让玩家最开心的方案.

您也可以尝试在给定遭遇中针对相同数字的退避策略,例如,如果玩家1在第一个回合中滚动接受它.要获得另一个,1他们需要1连续滚动2 秒.要获得第三名,1他们需要连续3 次,无限制.



11> samjudson..:

不幸的是,你要求的实际上是一个非随机数字生成器 - 因为你想要在确定下一个数字时考虑以前的结果.这不是随机数生成器工作的方式,我担心.

如果您希望每5次点击中有1次是关键,那么只需选择一个介于1和5之间的数字,并说该命中将是一个关键.



12> Michael Borg..:

mt_rand()基于Mersenne Twister实现,这意味着它可以获得您可以获得的最佳随机分布之一.

显然你想要的并不是随机性,所以你应该开始准确地指出你想要的东西.你可能会意识到你有相互矛盾的期望 - 结果应该是真正随机的而且不可预测的,但同时它们不应该表现出来自所述概率的局部变化 - 但随后它变得可预测.如果你连续设置了最多10个非crits,那么你刚刚告诉玩家"如果你连续有9个非crit,那么下一个将是100%确定的关键" - 你可能会根本没有随意性的麻烦.



13> Ed James..:

在如此少量的测试中,你应该期待这样的结果:

真正的随机性只能在巨大的设定尺寸上预测,因此很可能第一次翻转硬币并连续三次获得头部,但是在几百万次翻转时,你最终将大约50-50.


虽然仍然有可能在几百万次翻转之后,你仍然只能看到硬币的一面.虽然如果发生这种情况,你可能会坐得太近,无法实现无限的不可能性:P

14> Paulius..:

我看到很多答案建议跟踪以前生成的数字或者洗牌所有可能的值.

就个人而言,我不同意,连续3次惨叫是坏事.我也不同意连续15次非破坏是坏事.

我会通过在每个数字之后修改它自己的暴击几率来解决问题.示例(演示想法):

int base_chance = 20;
int current_chance = base_chance;

int hit = generate_random_number(0, 100) + 1; // anything from 1 to 100
if(hit < current_chance)//Or whatever method you use to check
{
    //crit!
    if(current_chance > base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 0.8; // decrease the crit chance for the NEXT hit.
}
else
{
    //no crit.
    if(current_chance < base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 1.1; // increase the crit chance for the NEXT hit.
    //raise the current_chance
}

你没有得到暴击的时间越长 - 你下一次暴击行动的可能性就越大.我包含的重置是完全可选的,它需要测试来判断它是否需要.在长期非暴击动作链之后,连续多次动作的暴击概率可能更高或可能不理想.

只要投入我的2美分......



15> Neil G..:

最重要的几个答案是很好的解释,所以我只关注一个算法,让你可以控制"坏条纹"的可能性,同时永远不会变得确定.这是我认为你应该做的:

不是指定p,伯努利分布的参数,即临界命中的概率,指定ab,β分布的参数,伯努利分布的"共轭先验".你需要跟踪AB,到目前为止关键和非关键命中的数量.

现在,要指定ab,确保a /(a + b)= p,是一个致命一击的机会.巧妙的是,(a + b)量化了你希望A /(A + B)与p一般接近的程度.

你做这样的采样:

p(x)是所述β分布的概率密度函数.它可以在很多地方使用,但您可以在GSL中找到它作为gsl_ran_beta_pdf.

S = A+B+1
p_1 = p((A+1)/S)
p_2 = p(A/S)

通过概率p_1 /(p_1 + p_2)从bernoulli分布中抽样来选择一个致命一击

如果您发现随机数有太多"坏条纹",请按比例放大ab,但在极限情况下,当ab转到无穷大时,您将获得之前描述的随机包方法.

如果你实现这个,请告诉我它是怎么回事!



16> Michael J..:

如果您想要一个不鼓励重复值的分布,您可以使用简单的重复拒绝算法.

例如

int GetRand(int nSize)
{
    return 1 + (::rand() % nSize);
}
int GetDice()
{
    static int nPrevious=-1;
    while (1) {
        int nValue = GetRand(6);
        // only allow repeat 5% of the time
        if (nValue==nPrevious && GetRand(100)<95)
            continue;
        nPrevious = nValue;
        return nValue;
    }
}

此代码在95%的时间内拒绝重复值,重复不太可能但不是不可能.从统计上来说它有点难看,但它可能会产生你想要的结果.当然,它不会阻止像"5 4 5 4 5"这样的分发.你可以得到更好的并且拒绝倒数第二(比如说)60%的时间和第三名(比如说)30%.

我不推荐这个好游戏设计.只是建议如何实现你想要的.

推荐阅读
夏晶阳--艺术
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有