This post显示了如何编写算法,一次吐出 n 中 k 元素的所有组合,从而避免排列。但是,如何编写一个算法 on demand ,给出下一个组合(很明显,没有预先计算和存储它们)?它将使用有序的符号集 n 和整数 k 进行初始化,然后调用它以返回下一个组合。
伪代码或一个好的英文叙述对我的目的来说没问题 - 除了Perl和C以及一些Java之外我还不能流利。
答案 0 :(得分:3)
原始字样
(跳至更新以下)
n
元素是整数1..n
。k
- 组合(此表示消除了k
- 组合内的排列。)n
元素)之间的词典顺序。换句话说,如果存在索引{i_1..i_k} < {j_1..j_k}
,则t
i_s = j_s
和s < t
i_t < j_t
。{i_1..i_k}
是k
- 组合,请将next{i_1..i_k}
定义为w.r.t的下一个元素。词典顺序。以下是如何计算next{i_1..i_k}
:
r
,以便i_r + 1 < i_{r+1}
i_k < n
,请设置r := k
k
- 组合等于{n-k+1, n-k+2,... ,n}
)r
满足第一个条件,请将next
设为{i_1, ..., i_{r-1}, i_r + 1, i_{r+1}, ..., i_k}
r = k
(第二个条件),请设置next := {i_1, ..., i_{k-1}, i_k + 1}.
更新(非常感谢@rici改进解决方案)
n
元素是整数1..n
。k
- 组合(此表示消除了k
- 组合内的排列。)n
元素)之间的词典顺序。换句话说,如果存在索引{i_1..i_k} < {j_1..j_k}
,则t
i_s = j_s
和s < t
i_t < j_t
。{i_1..i_k}
是k
- 组合,请将next{i_1..i_k}
定义为w.r.t的下一个元素。词典顺序。k
- 组合为{1..k}
,最大{n-k+1, n-k+2,... ,n}
。以下是计算next{i_1..i_k}
r
,以便i_r
可以增加1
。r
的值,并使用从i_r + 2
开始的连续值重置以下元素。更确切地说:
i_k < n
,请按i_k
增加1
(即将i_k
替换为i_k + 1
)i_k = n
,请找到r
这样的最大索引i_r + 1 < i_{r+1}
。然后按i_r
增加1
并将以下位置重置为{i_r + 2, i_r + 3, ..., i_r + k + 1 - r}
{n-k+1, n-k+2,... ,n}
注意算法的递归字符:每次递增最低有效位置时,尾部都会重置为按字典顺序排列的最小序列,该序列以刚递增的值开始。
Smalltalk代码
SequenceableCollection >> #nextChoiceFrom: n
| next k r ar |
k := self size.
(self at: 1) = (n - k + 1) ifTrue: [^nil].
next := self shallowCopy.
r := (self at: k) = n
ifTrue: [(1 to: k-1) findLast: [:i | (self at: i) + 1 < (self at: i+1)]]
ifFalse: [k].
ar := self at: r.
r to: k do: [:i |
ar := ar + 1.
next at: i put: ar].
^next
答案 1 :(得分:1)
这是一篇关于如何做到这一点的散文描述。从您最喜欢的迭代算法开始,生成所有组合。然后将每个循环变量转换为状态变量,并将其全部打包到一个类中。使用 k 和 n 构造类的实例,并根据算法初始化每个状态变量。
答案 2 :(得分:1)
您可以通过将这些算法转换为Iterator Pattern来实现大多数这些算法。这要求您在连续nextELement()
次调用之间保存算法的状态。
如果您的语言支持协同程序,则可以更轻松地转换代码。 Python和C#都有一个yield
关键字,可用于将控制权转移回调用函数,同时保留您正在执行的算法状态。