统一选择物品分配到箱子中

时间:2017-04-23 00:30:44

标签: algorithm math combinatorics

想象一下,你有 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选择,将进行多次。

2 个答案:

答案 0 :(得分:1)

以下是针对两种情况的两种算法。

您有大量可用内存,并且您会进行多次展示。在这种情况下,您可以使用内存预先计算并将n项目的所有可能展示位置存储到{ {1}}箱子。如果我们让mC(n, r)项目中r个项目的组合数量,而不重复且不考虑订单,那么可能的展示位置数量为n。 (该公式在组合学中非常标准,并使用stars and bars method)。在你的例子中,那是

C(m+n-1, m-1)

(如果MathJax在StackOverflow中可用,我可以使用标准数学符号使其看起来更漂亮。)在程序的设置中,可以使用这个想法的小递归例程生成这些放置 - 放置{{1将项目(C(5+3-1, 3-1) = C(7, 2) = 7!/2!/(7-2)! = 7/1*6/2 = 21 )放入第一个bin中,然后将剩余的k项放入剩余的0 <= k <= n个bin中。然后,每次要随机放置时,请选择介于1和n-k之间的随机数,并将其用作索引来选择放置。每个附加放置的时间成本只是一个随机数计算和一个阵列访问。你不可能比这更好。

你几乎没有可用的内存,你会做一个或几个展示位置。然后你可以选择一个迭代例程来计算多个组合系数。

首先,计算m-1个商品在C(m+n-1, m-1)个投放箱n中的可能展示位置数,然后从1到该组合数中选择一个随机数m。让C(m+n-1, m-1)为要放在第一个bin中的项目数。然后,r个分区中剩余k个项目,其中n-k个数量为m-1。现在从C(m+n-k-2, m-2)开始循环k。如果0的计数超过或等于k=0,我们决定在第一个bin中设置r项。如果不是,请将k=0增加1,然后按{组合数>减​​少k,并找到新r的新组合计数。如果该计数超过或等于k,我们决定在第一个bin中设置r项。如果没有,请将k增加一个......你明白了。当我们选择k的特定值时,我们将k替换为nn-k替换为m,请保留m-1,就像现在一样,并移动到下一个垃圾箱。

对此进行计数是r遍历项目的n次迭代,以及m次迭代和组合系数计算的m+n次迭代。唯一的内存使用是一些简单的变量和最终放置到m个bin中。你想要一个好的组合系数计算器例程。

后来补充:我已完全编码此例程,并找到了一种更好的方法来计算所涉及的概率而无需查找组合数。这减少了计算时间并完全达到了{{1如果我能找到一个好的函数来直接找到一个给出一定概率的值,那么这可以简化为m+n。但是我找不到这样的函数。我可以找到近似值,如果您愿意接近均匀分布的展示位置,而不是完全均匀的分布。)

让我知道如果您想要一些Python代码演示常规,但要明确您所处的情况并首先展示您自己的努力。

答案 1 :(得分:0)

五个相同的项目可以分布在三个不同的区域中,分别以3个 5 = 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],因为从不在当前bin中放入小于项目数除以箱数(这就是为什么上面例子中第一个箱子里没有1个项目的选项)。

(可以通过使用memoization来加速分区。)

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

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相关问题。)