Ruby - 如何在数组中生成 1% 几率的项目?

WAL*_*ISK 1 ruby random probability

我有 3 个分类稀有度,概率是这样的

class S has 10% probability

class A has 30% probability

class B has 60% probability
Run Code Online (Sandbox Code Playgroud)

所以我这样编码

class S has 10% probability

class A has 30% probability

class B has 60% probability
Run Code Online (Sandbox Code Playgroud)

我的客人的结果是非常正确的(cmiiw)

B
A
B
B
S
B
A
S
B
A
B
Run Code Online (Sandbox Code Playgroud)

但当我应该添加更多类别并将 S 类别概率更改为 1% 时,我变得很困惑

池现在变成

class S has 1% probability

class A has 29% probability

class B has 30% probability

class C has 40% probability
Run Code Online (Sandbox Code Playgroud)

我不确定我pool之前应该创建类似池的变量,因为 1% 是 1/10 不是整数。

急需帮助,谢谢!

pjs*_*pjs 6

考虑使用免费软件 gem aliastable它\xe2\x80\x99 是 AJ Walker 于 1974 年发布的别名方法的 Ruby 实现。注意:我是作者。

\n

更新:根据评论中的反馈(感谢@engineersmnky!),gem 现在可以优雅地处理离散均匀分布\xe2\x80\x94,其中所有概率都相等\xe2\x80\x94,基于下面描述的情况 1。它还已更新为使用惰性枚举生成值。

\n

当分布包含不等概率时,别名方法需要 O(n) 时间来创建包含 n 个结果的表,然后生成每个值需要 O(1) 时间。相比之下,从概率质量函数构建 CDF,然后对值进行二分搜索将分别花费 O(n) 和 O(log n) 时间。

\n

这个小演示展示了如何使用 gem:

\n
require \'aliastable\'\n\nvalues = %w(S A B C)\nprobs = [1r/100, 29r/100, 3r/10, 2r/5] # r => Rational\nmy_distribution = AliasTable.new(values, probs)\n\np my_distribution.take(1_000_000).tally\n
Run Code Online (Sandbox Code Playgroud)\n

产生如下结果

\n
{"C"=>400361, "B"=>300121, "A"=>289636, "S"=>9882}\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n

别名方法逻辑说明

\n

构建表

\n

当尝试从一组离散的n 个项目生成值时,有两种情况可以在恒定时间内完成这项工作:

\n
    \n
  1. n 个结果,所有结果的概率均为1/n。选择values[rand(n)]
  2. \n
  3. n = 2 并且两个结果具有不同的概率。可以使用简单的 if/else 语句进行二元选择。
  4. \n
\n

别名方法构建一个包含三行、primaryaliasprimary_probabilityn列的表。每个结果都分配给一列,作为该列\xe2\x80\x99sprimary值,其概率输入为primary_probability。该alias列的 被初始化为nil然后可以根据与主要相关的概率相对于平均概率1/n将该列分类为属于三个集合之一:

\n
    \n
  • 赤字设定:primary_probability < 1/n
  • \n
  • 剩余集:primary_probability > 1/n
  • \n
  • 奇偶校验集: primary_probability = 1/n
  • \n
\n

请注意,赤字集中 \xe2\x80\x9cmissing\xe2\x80\x9d 概率的总量恰好等于盈余集中 \xe2\x80\x9cexcess\xe2\x80\x9d 概率的总量,因此该表结构通过从盈余列之一窃取概率使赤字列之一达到平价来平衡账簿。primary被窃取的列的值将被添加为当前alias赤字列。条件概率用于计算主值拥有该列概率的比例并进行primary_probability相应调整。然后将该列迁移到奇偶校验集。此外,被盗概率的数量必须从选定的盈余列中递减,这可能使其仍然具有盈余,或者可能将其分类更改为赤字或平价。如果分类发生更改,请将其迁移到适当的集合。

\n

重复此过程,直到所有列都在奇偶校验集中。表构建算法保证在少于n次迭代内终止,因为每次迭代都会将一个赤字列(可能还有一个剩余列)迁移到奇偶校验集。

\n

使用表格

\n

一旦表构建完成,我们就有n列,每一列都有相等的总概率。每列都有一个主值,或者一个主值和一个别名,以及在我们进入该列的情况下选择主值的条件概率。则生成算法为:

\n
    \n
  • 以相同的概率选择任意一列。
  • \n
  • 根据我们在本列中选择主数据库的条件概率,返回主数据库或别名。
  • \n
\n

编码逻辑为:

\n
column = rand(n)\nif rand < primary_probability[column]\n  return primary[column]\nelse\n  return alias[column]\nend\n
Run Code Online (Sandbox Code Playgroud)\n

由于这两个步骤都是恒定时间,因此生成结果的总时间也是恒定的。

\n