【问题标题】:Uniformly selecting a distribution of items into bins将项目的分布均匀地选择到箱中
【发布时间】:2017-05-16 19:54:29
【问题描述】:

假设您有 n 个项目和 m 个垃圾箱。所有项目都相同,但垃圾箱不同。将物品随机分配到垃圾箱中的最快算法是什么?

例如 - 想象104 是将 5 件物品放入 3 个箱子中。 5 个物品有 21 种可能的放置方式进入 3 个箱子:

005  014  023
032  041  050
104  113  122
131  140  203
212  221  230
302  311  320
401  410  500

对于大量的物品和垃圾箱,我应该如何选择将n 物品放置到m 垃圾箱中,以便每个可能的位置都有相同的发生机会?对于给定的nm,选择将进行很多次。

【问题讨论】:

  • 这个展示位置会做多少次?换句话说,这会做一次还是只做几次,所以设置时间很重要,还是会做很多次,所以设置成本不重要,因为它会分散(摊销)?内存要求是什么?例如,是否可以预先计算所有可能的位置并将其存储在内存中,然后程序随机选择一个?您没有提供足够的细节来选择“最快的算法”,所以您的问题现在还不清楚。

标签: algorithm math combinatorics


【解决方案1】:

这里有两种算法适用于两种情况。

你有很多可用的内存,你会做很多展示。在这种情况下,你可以使用你的内存预先计算你的n项目的所有可能的展示位置并将其存储到m垃圾箱。如果我们让C(n, r) 是从n 项目中取出的r 项目的组合数,不重复也不考虑顺序,那么可能的放置数是C(m+n-1, m-1)。 (该公式在组合学中非常标准,并使用stars and bars method)。在你的例子中,那是

C(5+3-1, 3-1) = C(7, 2) = 7!/2!/(7-2)! = 7/1*6/2 = 21

(如果在 StackOverflow 中可以使用 MathJax,我可以使用标准数学符号使它看起来更漂亮。)在您的程序设置中,可以使用带有这个想法的小型递归例程生成这些放置 - 放置 k项目 (0 <= k <= n) 放入第一个箱子,然后将剩余的 n-k 项目放入剩余的 m-1 箱子中。然后每次你想要一个随机放置时,选择一个介于 1 和 C(m+n-1, m-1) 之间的随机数,并将其用作选择放置的索引。每个额外放置的时间成本只是一次随机数计算和一次数组访问。没有比这更好的了。

你的可用内存很少,你会做一个或几个放置。然后你可以用一个计算多个组合系数的迭代例程来选择你的随机放置。

首先,计算您的n 项目可能放置到m 箱中的数量C(m+n-1, m-1),然后从 1 到该组合数选择一个随机数 r。设k 是要放入第一个箱的项目数。然后将有n-k 剩余项目在m-1 箱中,其计数为C(m+n-k-2, m-2)。现在循环k,从0开始。如果 k=0 的计数超过或等于 r,我们决定在第一个 bin 中设置 k=0 项目。如果不是,则将k 增加一,将r 减少该组合计数,并为新的k 找到一个新的组合计数。如果该计数超过或等于 r,我们决定在第一个 bin 中设置 k 项目。如果没有,请将k 增加一,然后......你明白了。当我们选择了k 的特定值时,我们将n 替换为n-k,将m 替换为m-1,保持r 不变,然后移动到下一个bin。

对于 m+n 迭代和组合系数的计算,此计数是 n 通过项目迭代和 m 通过 bin 迭代。唯一的内存使用是一些简单的变量和最终放置到m bins 中。您需要一个好的组合系数计算器例程。

(稍后添加:我已经对这个例程进行了完整的编码,并找到了一种更好的方法来计算所涉及的概率,而无需找到组合的数量。这减少了计算时间并完全实现了m+n的顺序对于例程。如果我能找到一个好的函数来直接找到一个给出一定概率的值,这可以简化为订单m,但我找不到这样的函数。如果你愿意,我可以找到近似值具有接近均匀的展示位置分布,而不是完全均匀的分布。)

让我知道如果您想要一些 Python 代码来演示这两种例程,但请先说明您所处的情况并展示您自己的更多努力。

【讨论】:

    【解决方案2】:

    五个相同的项目可以以 35 = 243 种方式分布在三个不同的箱中,每种方式导致以下 21 种分布之一:

    500   410   320   311   221  
    050   401   302   131   212  
    005   140   230   113   122  
          104   203  
          041   032  
          014   023  
    

    您会注意到这里有两种机制在起作用:首先,数字 5 可以以 5 种不同的方式(列)分成最多 3 个部分,其次,每个这样的分区都有许多排列(行)。

    要枚举部分数量有限的分区,请使用递归算法,例如有 5 件物品和 3 个箱子:

    5 items in 1 bin  
    4 items in 1 bin + recurse with 1 item in 2 bins  
    3 items in 1 bin + recurse with 2 items in 2 bins
    2 items in 1 bin + recurse with 3 items in 2 bins
    

    只生成非上升序列,如 [2,2,1],而不是 [2,1,2] 或 [0,2,3],方法是永远不要将小于等于项目数除以箱数(这就是上例中第一个箱中没有 1 项选项的原因)。

    (使用记忆化可以加快分区速度。)

    要计算每个分区的概率(即排列数),将箱数的阶乘除以具有一定数量的物品的箱数的阶乘的乘积:

    5,0,0   3! / (1! x 2!)      = 3  
    4,1,0   3! / (1! x 1! x 1!) = 6  
    3,2,0   3! / (1! x 1! x 1!) = 6  
    3,1,1   3! / (1! x 2!)      = 3  
    2,2,1   3! / (2! x 1!)      = 3  
                                 --
                                 21
    

    然后,从1到21中选择一个随机数,并选择对应的分区及其排列;例如选择 13 意味着分区 [3,2,0] 及其第四个排列 [2,0,3]。

    因此,我们不是枚举所有(在 5:3 示例中为 243 个)选项或所有 (21) 个分布,而是枚举 (5) 个分区,如果没有更快的方法可以枚举(最多 6 个)排列第 n 个唯一排列。对于大量数字,这应该会产生巨大的影响。

    (有关其中一些步骤的详细信息和代码示例,请参阅this answer 相关问题。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 1970-01-01
      • 2011-04-04
      • 1970-01-01
      • 2021-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多