我知道如何计算一次取k个n个不同对象的组合总数,并替换:
(N + K-1)!/ K!/(N-1)!
我需要的是从有序列表中恢复第i个这样的组合的公式或算法。
假设我有a,b,c的所有组合的有序列表,一次取3个(所以n = 3且k = 3):
1 aaa 2 aab 3 aac 4 abb 5 abc 6 acc 7 bbb 8 bbc 9 bcc 10 ccc
如何计算此列表中的第i个(比如第7个)组合,而不先计算它们全部?如果我只对一些特定的组合感兴趣,那么对于除了最简单的情况之外的任何情况,枚举都将是非常低效的。例如,有64个项目的119,877,472个组合,每次6个。 不用说,我需要任意n,k和i的解决方案。 反向函数(给定组合,如何计算其指数)也很有意思。
我发现了一个类似的问题,但它是关于排列,而不是组合: I want to get a specific combination of permutation? 列出所有组合的方法有很多种,例如这里提到的: How to generate all permutations and combinations with/without replacement for distinct items and non distinct items (multisets) 但他们没有提供我需要的功能
答案 0 :(得分:1)
您感兴趣的算法非常容易实现。你应该理解的第一件事是为什么 C(k,n + k - 1)= C(n - 1,n + k - 1)=(n + k - 1)! / k! /(n - 1)!公式有效。公式表示从 n 中获取 k 项目的方式与从 n <
让我们说你的物体是一些颜色的球。 n 有不同的颜色,编号从1到n。您需要计算拥有 k 球的方式的数量。想象一下,最初 k 白球(没有任何颜色),所以你需要以不同的方式绘制它们。将球排成一排。从左侧选择一些 k 1 ≥0球,以颜色#1绘制,然后 k 2 ≥0我们在#2中绘制的球,依此类推......我们有Σk i = k 。一系列以#1颜色绘制的 k 1 球后跟 k 2 颜色#2,然后通过 k 3 的颜色#3等...
然而,我们可以以稍微不同的方式做同样的绘画。为了分离 k i-1 - 和 k i - 彩球,我们将使用分隔符即可。总的来说,我们应该将 n - 1 这样的分隔符放在球中。分隔符有序,分隔1色和2色球的分隔符应出现在另一个分隔2色和3色的球之前。如果某些 k i = 0 ,那么相应的分隔符将逐一出现。我们必须以某种方式安排分隔符和球。
有趣的是,我们现在可以想象, n - 1 分隔符和 k 球只是对象最初放在一行中。我们必须选择其中的 n - 1 来将所选对象声明为分隔符,或将 k 对象声明为球。并且可以应用众所周知的组合公式。
o
- 球
.
- 分隔符
a, b, c
- 颜色
我们有:
ooo.. => aaa
oo.o. => aab
oo..o => aac
o.oo. => abb
o.o.o => abc
o..oo => acc
.ooo. => bbb
.oo.o => bbc
.o.oo => bcc
..ooo => ccc
注意分隔符从右向左移动的模式。
现在讨论如何获得第p次安排。下面是高效的算法描述。请记住,我们有 k 球和 nd = n - 1 分隔符。我们将首先逐个放置分隔符,尝试最右边的位置。考虑将当前分隔符保留在当前位置,计算将剩余对象放在右侧的组合数,让数字为 N 。将 N 与 p 进行比较,如果 p 大于或等于 N ,则减少 p 通过 N ( &#34;转换&#34;一些第i 个对象第j 个分隔符我们有 N = C(nd - j,nd + k - i)方式的数量安排剩余的 k - i + j 球和 nd - j 分隔符。 由于我们经常会提到二项式系数,因此我们更好地进行预先计算。 可以相应地实现反向功能。您有每个分隔符的头寸。累积在将普通分隔符从最右侧位置移动到其位置时排列剩余对象的方式的数量。 将分隔符放在最右边的位置: 计算 N = C(1,1)= 1 ,p≥N因此我们将 p 减少 N 强>获得 p = 6 。与此同时,我们将当前分隔符1位置移至 计算 N = C(1,2)= 2 ,p≥N,将 p 减少 N 得到 p = 6 - 2 = 4 。移动获取 再次计算 N = C(1,3)= 3 ,p≥N,移动并减少 p 获得 p = 1 和 计算 N = C(1,4)= 4 , p < Ñ即可。好的,我们已找到第一个分隔符的最终位置,所以请将其保留在那里,并将第二个分隔符作为当前。 计算 N = C(0,0)= 1 ,p≥N, p = 1 - 1 = 0 ,移动, 计算 N = C(0,1)= 1 , p < N ,找到第二个分隔符的最终位置。得出的安排是 编辑#1。更改了算法说明并添加了示例。 答案 1 :(得分:0) 这是函数(未优化但正在工作):p <- p - N
)我们应该将当前分隔符向左移动。否则如果 p 低于 N 那么我们不会移动当前分隔符,而是继续尝试从最右侧位置再次移动它。请注意, p 排列从零开始。
实施例
3
个球,2
分隔符,找到7-th
排列(bbc
或.oo.o
)ooo..
。让第一个分隔符为 current 。 oo.o.
。o.oo.
。.ooo.
。.oo.o
。.oo.o
=&gt; bbc
。findcomb <- function(n, k, p) {
# n = nr of object types (colors, letters etc)
# k = number of objects (balls) to select
# p = 0-based index of target combination
# return = positions of delimiters at index p
nd <- n-1 #nr of delimiters: 1 - nr of colors
pos <- seq(n+k-nd, n+k-1) #original positions of delimiters, all at right
for (j in 1:(nd-1)) {
s <- 0 #cumulative nr of accounted-for combinations with this delimiter
while (TRUE) {
N <- choose(nd+k-pos[j], nd-j)
if (s + N <= p) {
pos[j] <- pos[j] - 1
s <- s + N
} else break
}
p <- p - s
}
#last delimiter:
pos[nd] <- pos[nd] - p
pos
}