繁体   English   中英

我可以使用返回加权布尔值的 function 实现加权随机性吗?

[英]Can I achieve weighted randomness with a function that returns weighted booleans?

我有一种模仿不公平硬币的方法。 您可以传入一个百分比,它会通过返回 boolean 来告诉您是否成功。 所以如果你用.25 调用它,它会在 25% 的时间内返回true

我试图弄清楚我是否可以使用这个 function 来创建一个加权随机性 function,它的工作原理如下: There is a 25% chance it returns x, a 40% chance it returns y, and a 35% chance it returns z. 这只是一个例子。 我希望 function 可以处理无限数量的字母,但加在一起的百分比应该等于 1。

诀窍是,我希望能够按照我刚才描述的方式来思考它。 换句话说:

result = function ({.25, x}, {.4, y}, {.35, z})

result应该是 x 25% 的时间,依此类推。 我可以用我的不公平币实现这个 function 吗?


这是我在下面的评论中的措辞。它可能会澄清我的要求:

如果我在这里犯了错误,请纠正我的逻辑,但是假设 XY 和 Z 都有。3333...我不能用我的不公平硬币来传递。3333...如果返回 true,那就意味着你得到了结果是 X。 如果返回 false,再次调用我的不公平。5 如果返回 true,则返回 Y,否则返回 Z。如果正确,如果数字 AREN'T.3333 并且如果有,我不知道如何使它工作三个以上

如果你有已知正面概率的硬币

假设你有一个 function unfairCoin(p) ,这是一个 function 以已知概率p产生正面,否则产生反面。 例如,它可以这样实现:

function unfairCoin(p) {
   return Math.random() < p ? True : false;
}

假设所有涉及的概率总和为 1,这是一个算法,可以解决您的问题给定unfairCoin

  1. cumu设置为 1。
  2. 对于从第一个开始的每个项目:
    1. 获取与所选项目相关的概率(称为p )并以概率p / cumu接受项目(例如,通过unfairCoin(p / cumu) )。 如果该项目被接受,则退回该项目。
    2. 如果该项目未被接受,则从cumu中减去p

该算法的预期时间复杂度取决于概率的顺序。 一般来说,该算法的时间复杂度是线性的,但如果将概率按降序排序,则预期时间复杂度是恒定的。

编辑(7 月 30 日):正如我刚刚发现的那样,Keith Schwarz 在Darts, Dice, and Coins中已经描述了这个精确的算法,在“用有偏差的硬币模拟加载的模具”中。 该页面还包含其正确性的证明。


另一种解决方案使用拒绝抽样,但需要使用公平的硬币投掷生成随机 integer:

  1. 在区间 [0, n) 内生成均匀随机 integer 索引,其中n为项目数。 例如,可以使用 J. Lumbroso 的Fast Dice Roller来做到这一点,它只使用公平的硬币投掷 ( unfairCoin(0.5) ); 请参阅下面的代码。 选择给定索引处的项目(从 0 开始)。
  2. 获取与所选项目相关的概率(称为p )并以概率p接受它(例如,通过unfairCoin(p) )。 如果该项目被接受,则退回该项目; 否则,go 执行步骤 1。

该算法的预期时间复杂度取决于最低概率和最高概率之间的差异。

给定每个项目的权重,除了前面给出的算法之外,还有许多其他方法可以做出加权选择; 请参阅我关于加权选择算法的说明

快速骰子滚子实施

以下是实现快速骰子滚轮的 JavaScript 代码。 请注意,它使用拒绝事件和循环来确保它是公正的。

function randomInt(minInclusive, maxExclusive) {
  var maxInclusive = (maxExclusive - minInclusive) - 1
  var x = 1
  var y = 0
  while(true) {
    x = x * 2
    var randomBit = (Math.random() < 0.5 ? 0 : 1)
    y = y * 2 + randomBit
    if(x > maxInclusive) {
      if (y <= maxInclusive) { return y + minInclusive }
      // Rejection
      x = x - maxInclusive - 1
      y = y - maxInclusive - 1
    }
  }
}

如果您的硬币正面概率未知

另一方面,如果你有一个 function COIN未知的概率输出正面而反之,那么有两个问题需要解决才能获得解决方案:

  1. 如何将有偏见的硬币变成公平的硬币。
  2. 如何将一个公平的硬币变成一个装好的骰子。

换句话说,任务是将有偏差的硬币变成装好的骰子。

让我们看看如何解决这两个问题。

从有偏见的硬币到公平的硬币

假设您有一个 function COIN()以未知概率输出正面,否则输出反面。 (如果已知硬币产生正面的概率为 0.5,那么您已经拥有一枚公平的硬币,可以跳过此步骤。)

在这里,我们可以使用 1951 年的冯诺依曼算法,将有偏见的硬币变成公平的硬币。 它是这样工作的:

  1. 翻转COIN()两次。
  2. 如果两个结果都是正面或两个结果都是反面,go 到步骤 1。
  3. 如果第一个结果是正面,另一个是反面,则以正面为最终结果。
  4. 如果第一个结果是反面,另一个是正面,则以反面作为最终结果。

现在我们有一个公平的硬币FAIRCOIN()

(请注意,还有其他以这种方式生产公平硬币的方法,统称为随机提取器,但冯诺依曼方法可能是最简单的。)

从公平的硬币到装好的骰子

现在,将公平硬币变成骰子的方法要复杂得多。 可以说有很多方法可以解决这个问题,其中最新的称为快速加载骰子滚轮,它只使用公平的硬币产生一个加载的骰子(实际上,它平均使用多达 6 次公平的硬币投掷超过生产每个加载的模具辊的最佳数量)。 该算法实现起来并不简单,但请参阅我的Python 实现Fast Loaded Dice Roller的作者的实现。

请注意,要使用快速加载骰子滚轮,您需要将每个概率表示为 integer 权重(例如您的示例中的 25、40、35)。

看看这个:

function weightedRandom(array) {
  // expected array: [[percent, var], [percent, var], ...] where sum of percents is 1
  var random=Math.random();
  var sofar=0;
  var index=-1;
  for(var i=0; i<array.length; i++) {
    if(sofar<random) index=i;
    sofar+=array[i][0];
  };
  return array[index][1];
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM