想象一下,你有 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
个投放箱中,以便每个可能的展示位置都有相同的机会?对于给定的n
和m
选择,将进行多次。
答案 0 :(得分:1)
以下是针对两种情况的两种算法。
您有大量可用内存,并且您会进行多次展示。在这种情况下,您可以使用内存预先计算并将n
项目的所有可能展示位置存储到{ {1}}箱子。如果我们让m
为C(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
替换为n
,n-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相关问题。)