按需算法从n返回k个元素的连续组合

时间:2015-02-26 03:00:28

标签: algorithm combinations

This post显示了如何编写算法,一次吐出 n k 元素的所有组合,从而避免排列。但是,如何编写一个算法 on demand ,给出下一个组合(很明显,没有预先计算和存储它们)?它将使用有序的符号集 n 和整数 k 进行初始化,然后调用它以返回下一个组合。

伪代码或一个好的英文叙述对我的目的来说没问题 - 除了Perl和C以及一些Java之外我还不能流利。

3 个答案:

答案 0 :(得分:3)

原始字样

(跳至更新以下)


  1. 我们假设n元素是整数1..n
  2. 以递增顺序表示每个k - 组合(此表示消除了k - 组合内的排列。)
  3. 现在考虑k组合(n元素)之间的词典顺序。换句话说,如果存在索引{i_1..i_k} < {j_1..j_k},则t
      所有i_s = j_s和的
    • s < t
    • i_t < j_t
  4. 如果{i_1..i_k}k - 组合,请将next{i_1..i_k}定义为w.r.t的下一个元素。词典顺序。
  5. 以下是如何计算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改进解决方案)

    1. 我们假设n元素是整数1..n
    2. 以递增顺序表示每个k - 组合(此表示消除了k - 组合内的排列。)
    3. 现在考虑k组合(n元素)之间的词典顺序。换句话说,如果存在索引{i_1..i_k} < {j_1..j_k},则t
        所有i_s = j_s和的
      • s < t
      • i_t < j_t
    4. 如果{i_1..i_k}k - 组合,请将next{i_1..i_k}定义为w.r.t的下一个元素。词典顺序。
    5. 请注意,对于此订单,最小k - 组合为{1..k},最大{n-k+1, n-k+2,... ,n}
    6. 以下是计算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关键字,可用于将控制权转移回调用函数,同时保留您正在执行的算法状态。