Math 是否有一个函数f(n)可以返回有序组合列表中的第n个组合而不重复?

Math 是否有一个函数f(n)可以返回有序组合列表中的第n个组合而不重复?,math,combinations,combinatorics,Math,Combinations,Combinatorics,当要从(n)中选择的元素数为5,选择的元素数(r)为3时,没有重复的组合如下所示: 随着n和r的增长,组合的数量迅速增加。对于(n,r)=(200,4),组合的数量为64684950 使用r嵌套for循环迭代列表很容易,其中每个for循环的初始迭代值都大于其嵌套的for循环的当前迭代值,如下面的jsfiddle示例所示: 我想要的是一个函数,它根据索引只计算一个组合。大概是这样的: tuple combination(i,n,r) { return [combination with in

当要从(n)中选择的元素数为5,选择的元素数(r)为3时,没有重复的组合如下所示:

随着n和r的增长,组合的数量迅速增加。对于(n,r)=(200,4),组合的数量为64684950

使用r嵌套for循环迭代列表很容易,其中每个for循环的初始迭代值都大于其嵌套的for循环的当前迭代值,如下面的jsfiddle示例所示:

我想要的是一个函数,它根据索引只计算一个组合。大概是这样的:

tuple combination(i,n,r) {
  return [combination with index i, when the number of elements to choose from is n and elements chosen is r]

有人知道这是否可行吗?

您首先需要对给定的
n
r
可用的所有组合集合进行某种排序,这样线性索引才有意义。我建议我们同意保持组合的递增顺序(或者,至少是单个元素的索引),如您的示例所示。那么,我们如何才能从线性指数转变为组合指数呢

让我们首先对这个问题建立一些直觉。假设我们有
n=5
(例如,集合
{0,1,2,3,4}
)和
r=3
。在这种情况下有多少独特的组合?答案当然是
5-choose-3
,其计算结果为
10
。由于我们将按顺序增加组合,考虑一下,一旦我们用完了<代码> 0 所有的组合,剩下多少组合。这必须是
4-choose-3
,或总共
4
。在这种情况下,如果我们开始寻找索引
7
处的组合,这意味着我们必须减去
10-4=6
,然后在集合
{1,2,3,4}
中搜索索引
1
处的组合。这个过程将继续,直到我们找到一个小于此偏移量的新索引

一旦这个过程结束,我们知道第一个数字。然后我们只需要确定剩余的
r-1
位数!因此,算法的形式如下(在Python中,但这应该不太难翻译)

从数学导入阶乘
def选择(n,k):
返回阶乘(n)/(阶乘(k)*阶乘(n-k))
在idx处的def组合(idx、元素、r):
如果len(elems)=r:
#我们正在寻找大小为r的列表中的r元素-因此,我们需要
#每个元素。
返回元素
如果len(elems)=0或len(elems)=偏移量:#组合不以第一个元素开始
返回组合_在_idx处(idx-偏移量,元素[1:],r)
#我们现在知道组合的第一个元素,但还不知道下一个元素
#r-1元素。这些也需要递归计算。
返回[elems[0]]+组合元素(idx,elems[1:],r-1)
用您的初始输入进行驾驶测试

N=5
R=3
对于范围内的idx(选择(N,R)):
打印(idx,组合_在_idx(idx,列表(范围(N)),R))
我发现,

0 [0, 1, 2]
1 [0, 1, 3]
2 [0, 1, 4]
3 [0, 2, 3]
4 [0, 2, 4]
5 [0, 3, 4]
6 [1, 2, 3]
7 [1, 2, 4]
8 [1, 3, 4]
9 [2, 3, 4]

其中,线性索引以零为基。

从结果的第一个元素开始。该元素的值取决于使用较小元素可以获得的组合数。对于每个这样较小的第一元素,与第一元素k的组合的数目为n− K− 1选择r− 1,可能会进行一些逐个更正。所以你可以对一组二项式系数求和。Wolfram Alpha计算了这样一个和,但结果中仍然有一个二项式系数。求最大的k,使得总和不超过给定的指数i,这是一个计算,你不能用像平方根这样简单的东西来做。您需要一个循环来测试可能的值,例如:

tuple combination(i,n,r) {
  return [combination with index i, when the number of elements to choose from is n and elements chosen is r]
def first_naive(i,n,r):
“”“查找第一个元素以及与该第一个元素的第一个组合的索引。”。
返回值和索引的元组。
示例:first_naive(8,5,3)返回(1,6),因为
索引8是[1,3,4],所以它以1开头,因为第一个组合
以1开头的是索引为6的[1,2,3]。
"""
s1=0
对于范围(n)内的k:
s2=s1+choose(n-k-1,r-1)
如果i
您可以使用二分法将O(n)循环迭代减少为O(logn)步数,这与大n特别相关。在这种情况下,我发现更容易考虑对列表末尾的项目进行编号。在n=5和r=3的情况下,您会得到
choose(2,2)=1
从2开始的组合,
choose(3,2)=3
从1开始的组合和
choose(4,2)=6
从0开始的组合。因此,在一般的
中,选择(n,r)
二项式系数,每一步增加n,并保持r。考虑到
sum(为范围(r,n+1)中的k选择(k,r))
choose(n+1,r+1)
,您最终可以得出如下等分条件:

def first_-bisect(i,n,r):
nCr=选择(n,r)
k1=r-1
s1=nCr
k2=n
s2=0
当k2-k1>1时:
k3=(k1+k2)//2
s3=nCr-选择(k3,r)

如果s3谢谢,拼写错误。很好,通过回答,谢谢!这在概念上是恰到好处的,所以谢谢你把它讲清楚。我认为大量组合所需的递归次数是禁止的,但这可能可以通过迭代而不是递归来解决。再次感谢@马丁:这里可以很容易地进行迭代,而不是递归。这就是我的代码所做的,但这个答案在我准备好之前就出现了,它涵盖了我的大部分想法。所以我不会发布我的代码,但我改投了这个。谢谢,这是gre