如何制作一个受先前事件影响的“随机”生成器?


37

我希望实现一个基于机会的系统,该系统受先前事件的影响。

背景:几年前,我记得《魔兽世界》的一个更新,宣布他们实施了一个新的机会计算器,可以抵消棘手的事件链。(例如,进行严厉打击或连续躲避几次)。这个想法是,如果您避开某个命中,那么您躲避下一个命中的机会就会减少,但是这将双向起作用。不回避命中同样会增加回避下一个命中的机会。这里的主要技巧是,在几次试验中,闪避几率仍然与他或她的状态表中给予该球员的百分比相对应。

这种系统当时非常吸引我,现在我处于需要这种解决方案的情况。

这是我的麻烦:

  • 我猜想我将能够找到有关实施这种系统的在线资源,但是我可能缺少找到它的相关口号。
  • 我还需要这种方法来适应不是二项式(即两个结果),而是包含4个互斥事件的系统。

我当前的方法类似于抽奖券系统。当事件发生时,我更改权重以支持所有其他事件。如果这四个事件具有同等可能性,那么这可能会起作用,但就我而言,这需要更加普遍。但是,由于普遍事件发生的频率更高,它使其他事件的权重偏移比预期的高得多,而且我似乎找不到为使平均故障单数量保持在事件的初始值附近所需的权重转移的数字。给定的。

一些方向指针或一个清晰的例子将不胜感激。


4
如果您希望获得一个非常细微或复杂的答案,则可以在Mathematics.SE上提出更多的要求。那里的数学家乐于回答有关概率的复杂问题。math.stackexchange.com
凯文-恢复莫妮卡

1
乔恩

6
较容易理解答案的数学网站是Programmers.SE。算法设计在Math 上并不是特别重要,您可能需要提出一个初步设计才能获得有用的输入。
Lilienthal

1
我同意Kevin和Lilienthal的观点,您可能会在这里得到更好的答案,但是阅读mklingen的答案后,我意识到这里描述的内容可以被建模为马尔可夫链,这可能是游戏开发人员了解的便捷工具。稍后,我将尝试详细介绍。
nwellcome,2015年

1
当我在这里用一些答案计算数字时,我发现存在许多不同的约束,而解决所有这些约束的解决方案可能比您需要的复杂。有关用例的更多详细信息可能有助于缩小最佳选择范围。例如,您的事件的概率是否相当相似(例如5个不同的结果,每个概率为20%)或相差很大(例如,10%错过80%命中10%严重)?您是否要最小化运行次数(例如,连续3次未中)或成群/等待(例如,在8次尝试中3次未中,或者在我遇到严重问题之前进行20次尝试)?
DMGregory

Answers:


19

基本上,您要的是一个“半随机”事件生成器,该生成器生成具有以下属性的事件:

  1. 预先指定每个事件发生的平均速率。

  2. 同一事件连续发生两次的可能性要比随机发生的可能性小。

  3. 这些事件不是完全可预测的。

一种方法是首先实现一个满足目标1和2 的非随机事件生成器,然后添加一些随机性以满足目标3。


对于非随机事件生成器,我们可以使用简单的抖动算法。具体而言,令p 1p 2,...,p n为事件1到n的相对似然性,令s = p 1 + p 2 + ... + p n为权重之和。然后,我们可以使用以下算法生成一个非随机的,最大均匀分布的事件序列:

  1. 最初,让e 1 = e 2 = ... = e n = 0。

  2. 要生成一个事件,每增加Ë p ,和输出事件ķ针对é ķ是最大的(破领带任何你想要的)。

  3. 递减Ë ķ通过小号,并从步骤2重复。

例如,给定三个事件A,B和C,且p A = 5,p B = 4和p C = 1,此算法将生成类似于以下输出序列的内容:

A B A B C A B A B A A B A B C A B A B A A B A B C A B A B A

请注意,这30个事件的序列如何精确地包含15个As,12 B和3C。它的分布不是理想-可以连续出现两次连续出现两个As的情况,这是可以避免的-但它接近了。


现在,要为该序列添加随机性,您可以使用几种(不一定是互斥的)选项:

  • 您可以按照菲利普的意见,并保持的“甲板” ñ即将举行的活动,对于一些适当大小的数ñ。每次您需要生成事件时,都从平台中选择一个随机事件,然后将其替换为上面的抖动算法输出的下一个事件。

    将其应用于上面的示例(N = 3)将产生例如:

    A B A B C A B B A B A B C A A A A B B A B A C A B A B A B A

    N = 10会产生更多随机现象:

    A A B A C A A B B B A A A A A A C B A B A A B A C A C B B B

    请注意,由于改组,常见事件A和B如何以更多的运行结束,而罕见的C事件仍然间隔良好。

  • 您可以将一些随机性直接注入到抖动算法中。例如,代替在步骤2中将e i增加p i,可以将其增加p i ×random(0,2),其中random(ab)是ab之间均匀分布的随机数;这将产生如下输出:

    A B B C A B A A B A A B A B A A B A A A B C A B A B A C A B

    或者您可以将e i增加p i + random(− cc),这将产生(对于c = 0.1× s):

    B A A B C A B A B A B A B A C A B A B A B A A B C A B A B A

    或者,对于c = 0.5× s

    B A B A B A C A B A B A A C B C A A B C B A B B A B A B C A

    请注意,与乘法事件相比,加性方案对罕见事件C的影响比对公共事件A和B的影响要大得多;这可能是或可能不希望的。当然,您还可以使用这些方案的某种组合,或者对增量进行任何其他调整,只要它保留e i平均增量等于p i的属性即可。

  • 或者,您有时可以通过将选定的事件k替换为随机事件k(根据原始权重p i选择)来扰动抖动算法的输出。只要您在步骤3中也使用与在步骤2中输出相同的k,则抖动处理仍将趋于使随机波动趋于均匀。

    例如,这是一些示例输出,随机选择每个事件的可能性为10%:

    B A C A B A B A C B A A B B A B A B A B C B A B A B C A B A

    这是一个示例,每个输出有50%的机会是随机的:

    C B A B A C A B B B A A B A A A A A B B A C C A B B A B B C

    你还可以考虑喂纯粹随机和抖动事件的搅和成甲板/混合池,如上所述,或者通过选择随机抖动算法ķ随机,由作为称重ë 秒(处理负权重为零)。

附言 以下是一些完全随机的事件序列,具有相同的平均速率,以进行比较:

A C A A C A B B A A A A B B C B A B B A B A B A A A A A A A
B C B A B C B A A B C A B A B C B A B A A A A B B B B B B B
C A A B A A B B C B B B A B A B A A B A A B A B A C A A B A

切线:由于评论中存在一些争论,即对于基于甲板的解决方案是否有必要在甲板重新装满之前让其清空,我决定对几种甲板填充策略进行图形比较:

情节
生成半随机硬币翻转的几种策略的图解(平均头与尾的比例为50:50)。横轴是翻转次数,纵轴是与期望比率的累计距离,以(头-尾)/ 2 =头-翻转/ 2来衡量。

图中的红线和绿线显示了两种非基于甲板的算法进行比较:

  • 红线,确定性抖动:偶数结果始终为正面,奇数结果始终为反面。
  • 绿线,独立的随机翻转:每个结果都是独立随机选择的,正面有50%的机会是正面,背面有50%的机会。

其他三行(蓝色,紫色和青色)显示了三种基于套牌的策略的结果,每种策略都是使用40张牌组成的套牌实施的,最初由20张“正面”牌和20张“尾部”牌填充:

  • 蓝线,空时填满:随机抽牌,直到牌组为空,然后用20张“正面”卡和20张“尾部”卡重新装满卡组。
  • 紫色线,半空时填满:随机抽牌,直到牌组剩下20张牌;然后用10张“正面”卡和10张“尾部”卡加满卡座。
  • 青色线条,连续填充:随机抽牌;偶数平局将立即替换为“正面”卡,奇数平局将替换为“尾部”卡。

当然,上面的图只是一个随机过程的单个实现,但是它具有一定的代表性。尤其是,您可以看到所有基于卡片组的过程都具有有限的偏差,并且与红色(确定性)线相当接近,而纯随机的绿色线最终会消失。

(实际上,蓝线,紫线和青色线偏离零的偏差严格受牌组大小限制:蓝线的偏离距离永远不会超过10步,紫色线只能偏离零距离15步。 ,而青色线最多可以从零开始漂移20步。当然,实际上,任何一条线实际上都不可能达到其极限,因为如果它们走得太远,它们很可能会趋近于零。关闭。)

乍一看,不同的基于套牌的策略之间没有明显的区别(尽管平均而言,蓝线与红线保持一定距离,而青色线与红线保持一定距离),但仔细检查蓝线确实揭示了一种独特的确定性模式:每40画一次(用虚线的灰色垂直线标记),蓝线恰好与红线零交汇。紫色和青色线不受严格限制,并且可以随时远离零。

对于所有基于平台的策略,即保持它们的变化为界的重要特点是,虽然卡的事实,从抽取的随机甲板,甲板被重新填充确定性。如果用于填充卡组的卡牌本身是随机选择的,那么所有基于卡组的策略将与纯随机选择(绿线)无法区分。


非常详尽的答案。在抖动算法中添加随机因素似乎很简单。:)
Sonaten

决定去回答你。:)但是我建议您将方法概述的其他内容放在顶部。根据您的回答,我将要做的是同时尝试“红色”和“紫色”解决方案。
Sonaten

53

不要掷骰子,发牌。

提取RNG的所有可能结果,将它们放在列表中,随机洗牌,然后以随机顺序返回结果。当您位于列表末尾时,请重复。

结果仍将均匀分布,但是除非列表的最后一个碰巧是下一个的第一个,否则各个结果也不会重复。

如果对于您的口味而言,这太可预测了,则可以使用列表n乘以可能结果的数量,然后将每个可能的结果放入该列表中n,然后再洗牌。或者,您可以在完全迭代列表之前重新排列列表。


1
查找“随机播放袋”(甚至在此站点上)
突然发生变化,2015年

3
这就是多少俄罗斯方块游戏避免让玩家饿死关键零件太长时间的原因。如果要在设定的时间间隔内控制事件的发生,请按照Philipp的建议清空袋子/甲板,这一点很重要。通过随手重新插入卡(或重新调整权重),可以以难以计算且容易出错的方式扭曲概率分布。
DMGregory

2
@DMGregory:实际上,在清空卡片组之前,最好混入新卡(事实上,我建议这样做,以使结果更自然且更难预测)。最重要的是要确保新的卡牌洗(平均)分数甲板上等于要绘制所需分数出来的吧。
Ilmari Karonen 2015年

4
Illmari Karonen:当您更换物品时,就可能会因为限制相同结果的运行或特定结果之间的长时间间隔而失去随机袋的好处。如果替换率等于目标概率分布,那么现在证明您处于与随机独立地生成每个结果相同的位置。如果它不等于目标概率分布,则可以以难以预测和平衡的方式扭曲有效概率-询问者恰好描述了这个问题的解决方法。
DMGregory

2
同意@DMGregory。通过改组新卡,您会使整个系统本身失效。卡片处理系统特别且完美地适合于所需的结果。例如,当你删除一个女王从甲板上(使用例如传统的卡片),画女王的概率降低,和绘图卡的可能性其他比女王增大。如果您愿意,这是一个自我调节系统。
Volte

17

您可以尝试马尔可夫随机图。考虑可能发生的每个事件都是图中的一个节点。从每个事件中,链接到可能在此之后发生的每个其他事件。这些链接中的每一个都通过称为过渡概率的权重进行加权。然后,您根据过渡模型执行图的随机游动。

例如,您可以具有一个表示攻击结果(严重击中,躲闪等)的图形。给定玩家的属性,将起始节点初始化为随机选择的起始节点(只需“掷骰子”即可)。然后,在下一次攻击时,在给定过渡模型的情况下,决定下一步将发生什么。

需要谨慎确定如何权衡过渡。一方面,从一个节点发出的所有转换都需要加起来的概率为1。您可以做的一件简单的事情是,从每个节点到每个其他节点进行转换,其权重等于这些事件发生的概率鉴于当前事件不会再次发生,因此是先验的

例如,如果您有三个事件:

  Critical, P = 0.1
  Hit,      P = 0.3
  Miss,     P = 0.6

您可以设置过渡模型,以使关键命中不会再次发生,只需将其概率质量重新均匀地重新分配给其他事件即可:

  Critical -> Critical,   P = 0.0
  Critical -> Hit,        P = 0.35
  Critical -> Miss,       P = 0.65

编辑:正如下面的评论所述,此模型还不够复杂,无法获得所需的行为。相反,您可能必须添加多个其他状态!


1
您建议的重新加权方案未保留每个状态的期望概率。用这些数字进行实证检验,错失发生的时间大约为41%,关键事件发生的时间大约为25%,与输入值相距甚远。转换成与概率成比例的其余状态(例如,小姐有25%的机率击中暴击率和75%的命中率)的情况稍好一些,漏率为44%,暴击率为17%,但仍然无法反映输入中的期望概率。
DMGregory

我忘了贝叶斯法则:(稍后再重新计算它可能无法维持的先验概率分布,因为它代表树叶出像CCHM或CHHM或很可能MMHM等可能的序列的过渡模式。
mklingen

关于极高和极低的重量,“无重复”约束可能会束手无策。如果您希望每10次尝试中就有1次是关键攻击,则此方法可以满足的唯一方法是交替5次未命中和5次命中,这会使命中和未命中概率向其平均值倾斜。没有连续未命中的序列不能满足此处输入的要求。
DMGregory

4
@mklingen,我同意DMGregory的观点,这里“严格禁止重复”是不可取的。相反,他们希望具有相同结果的长链的概率比具有统一随机概率的概率要小。您可以使用看起来像这样的Markov链(定向)来执行此操作。这使用多个状态来表示重复事件,其中从“命中1”过渡到“命中2”和“命中2”过渡到“命中3+”的概率下降,并且过渡回“命中1”和“暴击”的概率1英寸上升。
nwellcome,2015年

@nwellcome是个好主意。
mklingen

3

这是我在C#中创建的实现,该实现将:

  • 根据概率激活事件
  • 调整那些概率以减少重复发生事件的机会
  • 与原始概率相差不远

我添加了一些评论,以便您可以看到我在做什么。

    int percentageEvent1 = 15; //These are the starter values. So given a scenario, the
    int percentageEvent2 = 40; //player would have around a 15 percent chance of event
    int percentageEvent3 = 10; //one occuring, a 40 percent chance of event two occuring
    int percentageEvent4 = 35; //10 percent for event three, and 35 percent for event four.

    private void ResetValues()
    {
        percentageEvent1 = 15;
        percentageEvent2 = 40;
        percentageEvent3 = 10;
        percentageEvent4 = 35;
    }

    int resetCount = 0; //Reset the probabilities every so often so that they don't stray too far.

    int variability = 1; //This influences how much the chance of an event will increase or decrease
                           //based off of past events.

    Random RandomNumberGenerator = new Random();

    private void Activate() //When this is called, an "Event" will be activated based off of current probability.
    {
        int[] percent = new int[100];
        for (int i = 0; i < 100; i++) //Generate an array of 100 items, and select a random event from it.
        {
            if (i < percentageEvent1)
            {
                percent[i] = 1; //Event 1
            }
            else if (i < percentageEvent1 + percentageEvent2)
            {
                percent[i] = 2; //Event 2
            }
            else if (i < percentageEvent1 + percentageEvent2 + percentageEvent3)
            {
                percent[i] = 3; //Event 3
            }
            else
            {
                percent[i] = 4; //Event 4
            }
        }
        int SelectEvent = percent[RandomNumberGenerator.Next(0, 100)]; //Select a random event based on current probability.

        if (SelectEvent == 1)
        {
            if (!(percentageEvent1 - (3 * variability) < 1)) //Make sure that no matter what, probability for a certain event
            {                                                //does not go below one percent.
                percentageEvent1 -= 3 * variability;
                percentageEvent2 += variability;
                percentageEvent3 += variability;
                percentageEvent4 += variability;
            }
        }
        else if (SelectEvent == 2)
        {
            if (!(percentageEvent2 - (3 * variability) < 1))
            {
                percentageEvent2 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent3 += variability;
                percentageEvent4 += variability;
            }
        }
        else if (SelectEvent == 3)
        {
            if (!(percentageEvent3 - (3 * variability) < 1))
            {
                percentageEvent3 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent2 += variability;
                percentageEvent4 += variability;
            }
        }
        else
        {
            if (!(percentageEvent4 - (3 * variability) < 1))
            {
                percentageEvent4 -= 3 * variability;
                percentageEvent1 += variability;
                percentageEvent2 += variability;
                percentageEvent3 += variability;
            }
        }

        resetCount++;
        if (resetCount == 10)
        {
            resetCount = 0;
            ResetValues();
        }

        RunEvent(SelectEvent); //Run the event that was selected.
    }

希望对您有所帮助,请在注释中建议对此代码进行改进,谢谢!


1
这种重新加权方案往往会导致事件发生的可能性相等。定期重置权重实际上只是一个创可贴,可限制权重的恶化,同时确保十分之一的掷骰数不会因重新加权而受益。另外,有一个算法说明:您要填写100个条目的表来进行随机选择,这浪费了很多工作。取而代之的是,您可以生成随机滚动,然后遍历您的4个结果,并随即对它们的概率求和。一旦滚动小于总和,就可以得到结果。无需填写表格。
DMGregory

3

让我概括一下麦克林根的答案。基本上,您想实现Gambler's Fallacy,尽管我将在此处提供一个更通用的方法:

说有n可能发生的事件p_1, p_2, ..., p_n。事件i发生时,事件发生的概率应按一定比例重新调整0≤a_i≤1/p_i(后者很重要,否则您最终获得的概率将大于一个,而其他事件必须具有负概率,这基本上意味着“ ”事件。)通常a_i<1。例如a_i=p_i,您可以选择,这表示事件第二次发生的概率是事件连续发生两次的原始概率,例如,第二次抛硬币的概率为1/4而不是1/2。另一方面,您也可以设置一些a_i>1,这意味着触发“运气/不幸”。

所有其他事件相对于其他事件应保持相等的概率,即它们都必须用相同的因子重新缩放,以使b_i所有概率的总和等于一个,即

1 = a_i*p_i + b_i*(1-p_i)  # Σ_{j≠i) p_j  = 1 - p_i
 b_i = (1 - a_i*p_i) / (1 - p_i).   (1)

到目前为止,如此简单。但是,现在让我们增加另一个要求:考虑到两个事件的所有可能序列,从中提取的单事件概率应为原始概率。

        / p_i * b_i * p_j  (ji)
p_ij = <
        \ a_i * (p_i     (j=i)

表示事件j在事件之后发生的概率,i并指出p_ij≠p_ji除非b_i=b_j (2)(通过(1)暗示a_j = 1 - a_i*p_i + (1-a_i)*p_i/p_j)。这也是贝叶斯定理的要求,这也意味着

Σ_j p_ij = p_i * b_i * (1 - p_i) + a_i * (p_i
         = b_i * p_i + (a_i - b_i) * (p_i
         = p_i  # using (1)

随心所欲。只要注意这意味着一个a_i解决所有其他问题。


现在,让我们看看当多次应用此过程时,即对于三个或更多事件的序列,会发生什么。选择第三事件的操纵概率基本上有两种选择:

a)忘记第一个事件和装配,就好像只有第二个事件发生一样,即

         / p_ij * a_j * p_j  (j=k)
p_ijk = <
         \ p_ij * b_j * p_l  (jk)

请注意,这通常违反贝叶斯规则,因为例如p_jik≠p_ikj在大多数情况下。

b)将概率p_ij(对于固定i)用作新概率pi_j,从中可以获取新的概率pi_jkk以便下次发生事件。是否修改ai_j取决于您,但是请注意,bi_j由于修改,新的内容肯定有所不同pi_j。再一次,ai_j可能的选择可能受到要求ijk以相同概率发生的所有排列的限制。让我们来看看...

         / p_ij * bi_j * pi_k  (jk)
p_ijk = <
         \ (p_ij * ai_j      (j=k)

         / b_i * bi_j * p_i * p_j * pi_k  (ijki)
         | b_i * ai_j * p_i * (p_j      (ij=k)
      = <  a_i * (p_i * bi_i * pi_k     (i=jk)
         | b_i * p_i * bi_j * p_k * pi_i  (i=kj)
         \ a_i * ai_i * (p_i * pi_i     (i=k=j)

其循环排列,在每种情况下必须相等。

恐怕我要继续这样做要等一会儿...


根据经验进行测试,这仍然会导致许多次运行中输入概率的失真。例如,如果a_i / p_i = 0.5(并使用mklingen的答案中的数字),则60%的输入未命中率将变为50.1%的观察到的比率,而10%的输入临界率将被观察为13.8%。您可以通过将转换矩阵转换为高功率​​来验证这一点。将a_i:p_i的比率选择为接近1会导致较小的失真,但在减少行程方面的有效性也会降低。
DMGregory

@DMGregory好点:您不能简单地利用转换矩阵的幂。我将在稍后扩展我的答案
Tobias Kienzler 2015年

@DMGregory我开始描述整个过程(变体b)),但是它变得非常乏味,而且我的时间很短:/
Tobias Kienzler 2015年

1

我认为最好的选择是使用随机加权的项目选择。有对C#实现这里,但他们可以很容易找到或其它语言制作为好。

这个想法是要减少每次选择的权重,并在每次不选择的时候增加权重。

例如,如果您将选择的选项的权重减小,而将NumOptions-1其他每个选项的权重增加1 (请注意删除权重<0的项目,并在它们升至0以上时将其读取),则每个选项都会被近似地选择在很长一段时间内次数相同,但是最近选择的选项被选择的可能性将大大降低。


正如许多其他答案所建议的那样,使用随机排序的问题在于,在选择了每个选项之后,您可以100%地确定接下来将选择哪个选项。那不是很随机。


1

我的答案不正确,我的测试有缺陷。

我将这个答案留在此处进行讨论和评论,以指出该设计中的缺陷,但实际测试不正确。

您要寻找的是加权权重:您需要根据先前的结果进一步调整(加权)四个可能结果的权重,同时总体上仍要保持适当的权重。

实现此目的的最简单方法是,通过减少特定卷值的权重并增加其他权重更改每个卷的所有权重

例如,假设您有4个权重:Fumble,Miss,Hit和Crit。假设您需要的总权重为Fumble = 10%,Miss = 50%,Hit = 30%和Crit = 10%。

如果您使用随机数产生器(RNG)产生1到100之间的值,然后将该值与该范围内的值进行比较(1-10失败,11-60失误,61-90命中,91-100暴击) ),您正在生成一个单独的纸卷。

如果,当你说滚,你再即时调整基于卷,你会被未来的加权滚动值的范围,但你还需要由同一降低轧制重量金额,你通过增加其他的权重。因此,在上面的示例中,您将轧制重量减少了3,其他重量分别增加了1。

如果对每个卷都执行此操作,您仍然有机会出现条纹,但是条纹会大大减少,因为对于每个卷,您增加了将来的卷将是当前卷以外的任何其他卷的机会。您可以通过增加/减小权重(例如,将电流减少6或将其他电流增加2)来增加此效果,从而进一步减少出现条纹的机会。

我运行了一个快速的应用程序来验证此方法,并使用这些权重进行了32000次迭代后,生成了以下图形。上方的图表显示了每一卷的4个权重立即值,下方的图表显示了汇总到该点的每种类型的结果的总计数。

如您所见,权重在其期望值附近略有波动,但是总权重保持在期望范围内,并且在初始数字的初始变化确定之后,结果几乎完美地符合了我们期望的百分比。

请注意,此示例是使用.NET System.Random类生成的,它实际上并不是那里较好的RNG之一,因此,使用更好的RNG可能会获得更准确的结果。还要注意,使用该工具可以绘制出的最大结果为32000,但是我的测试工具能够以相同的总体模式生成超过5亿个结果。


请注意,这仅在您的+ 1 / -3是相对于原始权重而不是最近使用的权重应用时才有效。(像这样连续不断地修改权重会使它们向等概率漂移)。虽然从长远来看,这使目标概率保持在目标范围内,但减少运行的可能性却很小。考虑到我已经错过了一次,因此该计划连续两次错过的机会是22%,而独立抽奖则是25%。增加权重偏移以获得更大的效果(例如+ 3 / -9)会导致长期可能性产生偏差。
DMGregory

实际上,每次处理卷材时,以上提供的数据都会将+ 1 / -3应用于最近的重量。因此,如果您在最初的50%体重下错过一次,那么下一个体重将为47%,如果再次错过,则接下来的体重将为44%,依此类推。它确实减少了运行(单独的指标是跟踪运行,发现运行减少了多达24%),但是由于该方案仍然有很大的机会使4个权重中的每一个都具有非零概率(例如,连续放置四个糖果将使重锤的发生几率为零。
David C Ellis

如果这是您的意图,那么您的实现就有一个错误。看一下图表-笨拙的重量只会在7到11之间跳动,没有其他值。我使用您描述的连续修改进行了仿真,并且图形完全不同,在前一百次试验中,每种状态的概率均朝着25%收敛。
DMGregory

丹吉特,确实如您所指出的那样被窃听了。好吧,点击这个答案。
David C Ellis

@DavidCEllis您是说您的实现存在缺陷,还是这个想法本身是?我的直觉是大致上您所描述的模型(将绘制时的概率调低,逐渐将所有概率恢复为原始值),这对我仍然有意义。
dimo414

0

您可以做本质上是过滤器的事情。跟踪过去的n个事件。概率是应用于这些事件的某些过滤器中的一些。第0个过滤器是基本概率,如果为0,则表示躲避,如果为1,则表示失败。假设基数为25%,并且每次迭代过滤器减少一半。您的过滤器将是:

[.25 .125 .0625 .03125] 

如果愿意,请继续。该方案的总体概率略高于.25的基本概率。实际上,在给定相同方案的情况下,概率为(我称x为真实概率,p为输入的概率):

x=p+(1-x)*(p/2+p/4+p/8)

求解x,发现答案是p(1+1/2+1/4+1/8)/(1+p(1/2+1/4+1/8),或者对于我们给定的情况x=0.38461538461。但是,您真正想要的是找到给定x的p。事实证明这是一个更困难的问题。如果您假设使用无限滤镜,则问题将变为x+x*p=2*pp=x/(2-x)。因此,增加过滤器,您可以求解出一个数值p,该数值平均会给您带来相同的结果,但是速率取决于最近发生了多少成功。

基本上,您使用先前的值来确定此轮的接受阈值,并采用随机值。然后在给定滤波器的情况下产生下一个随机值。


-1

就像您自己提出建议一样,一种方法是实现加权随机。想法是制作一个随机数(或结果)生成器,在此可以更改权重和结果。

这是Java的实现。

import java.util.Map;
import java.util.Random;

/**
 * A psuedorandom weighted outcome generator
 * @param <E> object type to return
 */
public class WeightedRandom<E> {

    private Random random;
    private Map<E, Double> weights;

    public WeightedRandom(Map<E, Double> weights) {
        this.random = new Random();
        this.weights = weights;
    }

    /**
     * Returns a random outcome based on the weight of the outcomes
     * @return
     */
    public E nextOutcome() {
        double totalweight = 0;

        // determine the total weigth
        for (double w : weights.values()) totalweight += w;

        // determine a value between 0.0 and the total weight
        double remaining = random.nextDouble() * totalweight;

        for (E entry : weights.keySet()) {
            // subtract the weight of this entry
            remaining -= weights.get(entry);

            // if the remaining is smaller than 0, return this entry
            if (remaining <= 0) return entry;
        }

        return null;
    }

    /**
     * Returns the weight of an outcome
     * @param outcome the outcome to query
     * @return the weight of the outcome, if it exists
     */
    public double getWeight(E outcome) {
        return weights.get(outcome);
    }

    /**
     * Sets the weight of an outcome
     * @param outcome the outcome to change
     * @param weight the new weigth
     */
    public void setWeight(E outcome, double weight) {
        weights.put(outcome, weight);
    }
}

编辑 如果要自动调整权重,例如当结果为B时增加A的机会。您可以选择

  1. 更改nextOutcome()方法的行为,以便根据结果修改权重
  2. 用于setWeight()根据结果​​修改权重。

我认为您可能误解了这个问题:OP并不是在问如何生成加权随机结果,而是在如何调整权重以减少同一结果连续出现多次的可能性。
Ilmari Karonen

我知道,我已更改了一些答案,以解释使用此系统将如何实现。
erikgaal 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.