从n返回k个元素的所有组合的算法

Algorithm to return all combinations of k elements from n

我想编写一个函数,它将字母数组作为参数,并选择一些字母。

假设您提供一个8个字母的数组,并希望从中选择3个字母。然后你应该得到:

1
8! / ((8 - 3)! * 3!) = 56

返回的数组(或单词),每个数组由3个字母组成。


《计算机编程艺术》第4卷:第3分册有大量这样的内容,可能比我描述的更适合你的特定情况。

格雷码

您将遇到的一个问题当然是内存问题,而且速度非常快,在您的集合中有20个元素——20c3=1140。如果你想在集合上迭代,最好使用一个修改过的灰色代码算法,这样你就不会把它们都保存在内存中。它们从前面生成下一个组合,避免重复。其中有许多用于不同的用途。我们想最大化连续组合之间的差异吗?减少?等等。

一些描述灰色代码的原始文件:

  • 哈密顿路径及其最小变化算法
  • 相邻互通式立交组合生成算法
  • 以下是其他一些涉及该主题的论文:

  • EADES,Hickey,read-adjacent交换组合生成算法(PDF,代码为Pascal)的有效实现
  • 组合发电机
  • 组合格雷码综述(后记)
  • 一种灰色码的算法
  • 蔡斯的旋转(算法)

    Phillip J Chase,"算法382:n个对象中m个的组合"(1970)

    C语言中的算法…

    词典编纂顺序组合索引(Buckles算法515)

    还可以通过其索引引用组合(按词典编纂顺序)。意识到索引应该是基于索引从右到左的一些变化,我们可以构建一些应该恢复组合的内容。

    所以,我们有一套1,2,3,4,5,6…我们需要三种元素。假设1,2,3我们可以说元素之间的差异是1,有序和最小的。1,2,4有一个变化,在字典上是数字2。因此,最后一个位置的"变化"数量说明了词典编纂顺序的一个变化。第二个位置,一个变化1,3,4有一个变化,但由于它在第二个位置(与原始集合中的元素数量成比例),所以变化更多。

    我描述的方法是一种解构主义,从集合到索引,我们需要做相反的事情——这要复杂得多。这就是扣环解决问题的方法。我写了一些C来计算它们,只是做了一些小的改动——我使用集合的索引而不是数字范围来表示集合,所以我们总是从0…n开始工作。注:

  • 由于组合无序,1,3,2=1,2,3——我们将它们排序为词典编纂。
  • 此方法有一个隐式0,用于启动第一个差异集。
  • 按字典编纂顺序排列的组合索引(McCaffrey)

    还有另一种方法:它的概念更容易理解和编程,但它没有扣环的优化。幸运的是,它也不会产生重复的组合:

    使i = C(x_1,k) + C(x_2,k-1) + ... + C(x_k,1)最大化的集合x_k...x_1 in N,其中C(n,r) = {n choose r}最大化。

    例如:27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)。所以,第27个词汇编纂组合是:1,2,5,6,这些是你想看的任何集合的索引。下面的示例(ocaml)需要choose函数,留给读卡器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    (* this will find the [x] combination of a [set] list when taking [k] elements *)
    let combination_maccaffery set k x =
        (* maximize function -- maximize a that is aCb              *)
        (* return largest c where c < i and choose(c,i) <= z        *)
        let rec maximize a b x =
            if (choose a b ) <= x then a else maximize (a-1) b x
        in
        let rec iterate n x i = match i with
            | 0 -> []
            | i ->
                let max = maximize n i x in
                max :: iterate n (x - (choose max i)) (i-1)
        in
        if x < 0 then failwith"errors" else
        let idxs =  iterate (List.length set) x k in
        List.map (List.nth set) (List.sort (-) idxs)

    一个简单的小组合迭代器

    以下两种算法用于教学目的。它们在所有组合上实现迭代器和(更一般的)文件夹。它们尽可能快,具有复杂性o(nck)。内存消耗受k的约束。

    我们将从迭代器开始,它将为每个组合调用用户提供的函数

    1
    2
    3
    4
    5
    let iter_combs n k f =
      let rec iter v s j =
        if j = k then f v
        else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
      iter [] 0 0

    更一般的版本将从初始状态开始调用用户提供的函数以及状态变量。因为我们需要在不同的状态之间传递状态,所以不会使用for循环,而是使用递归,

    1
    2
    3
    4
    5
    6
    7
    8
    let fold_combs n k f x =
      let rec loop i s c x =
        if i < n then
          loop (i+1) s c @@
          let c = i::c and s = s + 1 and i = i + 1 in
          if s < k then loop i s c x else f c x
        else x in
      loop 0 0 [] x


    C中:

    1
    2
    3
    4
    5
    6
    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
    {
      return k == 0 ? new[] { new T[0] } :
        elements.SelectMany((e, i) =>
          elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
    }

    用途:

    1
    var result = Combinations(new[] { 1, 2, 3, 4, 5 }, 3);

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    123
    124
    125
    134
    135
    145
    234
    235
    245
    345


    JAVA短解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import java.util.Arrays;

    public class Combination {
        public static void main(String[] args){
            String[] arr = {"A","B","C","D","E","F"};
            combinations2(arr, 3, 0, new String[3]);
        }

        static void combinations2(String[] arr, int len, int startPosition, String[] result){
            if (len == 0){
                System.out.println(Arrays.toString(result));
                return;
            }      
            for (int i = startPosition; i <= arr.length-len; i++){
                result[result.length - len] = arr[i];
                combinations2(arr, len-1, i+1, result);
            }
        }      
    }

    结果将是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    [A, B, C]
    [A, B, D]
    [A, B, E]
    [A, B, F]
    [A, C, D]
    [A, C, E]
    [A, C, F]
    [A, D, E]
    [A, D, F]
    [A, E, F]
    [B, C, D]
    [B, C, E]
    [B, C, F]
    [B, D, E]
    [B, D, F]
    [B, E, F]
    [C, D, E]
    [C, D, F]
    [C, E, F]
    [D, E, F]


    我可以介绍一下这个问题的递归python解决方案吗?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def choose_iter(elements, length):
        for i in xrange(len(elements)):
            if length == 1:
                yield (elements[i],)
            else:
                for next in choose_iter(elements[i+1:len(elements)], length-1):
                    yield (elements[i],) + next
    def choose(l, k):
        return list(choose_iter(l, k))

    示例用法:

    1
    2
    >>> len(list(choose_iter("abcdefgh",3)))
    56

    我喜欢它的简单。


    假设您的字母数组如下所示:"abcdefgh"。你有三个索引(i,j,k),表示你要用哪个字母来表示当前单词,你从以下开始:

    1
    2
    3
    A B C D E F G H
    ^ ^ ^
    i j k

    首先你改变k,所以下一步看起来是这样的:

    1
    2
    3
    A B C D E F G H
    ^ ^   ^
    i j   k

    如果你到了终点,你继续改变j,然后再改变k。

    1
    2
    3
    4
    5
    6
    7
    A B C D E F G H
    ^   ^ ^
    i   j k

    A B C D E F G H
    ^   ^   ^
    i   j   k

    一旦你达到G,你也开始改变I。

    1
    2
    3
    4
    5
    6
    7
    8
    A B C D E F G H
      ^ ^ ^
      i j k

    A B C D E F G H
      ^ ^   ^
      i j   k
    ...

    用代码写的这个看起来像那样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void print_combinations(const char *string)
    {
        int i, j, k;
        int len = strlen(string);

        for (i = 0; i < len - 2; i++)
        {
            for (j = i + 1; j < len - 1; j++)
            {
                for (k = j + 1; k < len; k++)
                    printf("%c%c%c
    ", string[i], string[j], string[k]);
            }
        }
    }


    以下递归算法从有序集中选取所有k元素组合:

    • 选择组合的第一个元素i
    • i与从大于i的元素集合中递归选择的k-1元素的每个组合结合起来。

    对集合中的每个i重复上述步骤。

    重要的是,您选择其他元素大于i,以避免重复。这样,将只选取一次[3,5],因为[3]与[5]组合,而不是两次(条件消除了[5]+[3])。如果没有这个条件,你会得到变化而不是组合。


    我发现这个线程很有用,我想我可以添加一个JavaScript解决方案,您可以将其放入Firebug。根据JS引擎的不同,如果启动字符串很大,可能需要一些时间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function string_recurse(active, rest) {
        if (rest.length == 0) {
            console.log(active);
        } else {
            string_recurse(active + rest.charAt(0), rest.substring(1, rest.length));
            string_recurse(active, rest.substring(1, rest.length));
        }
    }
    string_recurse("","abc");

    输出应如下:

    1
    2
    3
    4
    5
    6
    7
    abc
    ab
    ac
    a
    bc
    b
    c


    在C++中,下面的例程将产生范围(第一,最后)之间的所有长度组合(第一,k):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #include

    template <typename Iterator>
    bool next_combination(const Iterator first, Iterator k, const Iterator last)
    {
       /* Credits: Mark Nelson http://marknelson.us */
       if ((first == last) || (first == k) || (last == k))
          return false;
       Iterator i1 = first;
       Iterator i2 = last;
       ++i1;
       if (last == i1)
          return false;
       i1 = last;
       --i1;
       i1 = k;
       --i2;
       while (first != i1)
       {
          if (*--i1 < *i2)
          {
             Iterator j = k;
             while (!(*i1 < *j)) ++j;
             std::iter_swap(i1,j);
             ++i1;
             ++j;
             i2 = k;
             std::rotate(i1,j,last);
             while (last != j)
             {
                ++j;
                ++i2;
             }
             std::rotate(k,i2,last);
             return true;
          }
       }
       std::rotate(first,k,last);
       return false;
    }

    它可以这样使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <string>
    #include <iostream>

    int main()
    {
        std::string s ="12345";
        std::size_t comb_size = 3;
        do
        {
            std::cout << std::string(s.begin(), s.begin() + comb_size) << std::endl;
        } while (next_combination(s.begin(), s.begin() + comb_size, s.end()));

        return 0;
    }

    这将打印以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    123
    124
    125
    134
    135
    145
    234
    235
    245
    345


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    static IEnumerable<string> Combinations(List<string> characters, int length)
    {
        for (int i = 0; i < characters.Count; i++)
        {
            // only want 1 character, just return this one
            if (length == 1)
                yield return characters[i];

            // want more than one character, return this one plus all combinations one shorter
            // only use characters after the current one for the rest of the combinations
            else
                foreach (string next in Combinations(characters.GetRange(i + 1, characters.Count - (i + 1)), length - 1))
                    yield return characters[i] + next;
        }
    }


    python中的简短示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def comb(sofar, rest, n):
        if n == 0:
            print sofar
        else:
            for i in range(len(rest)):
                comb(sofar + rest[i], rest[i+1:], n-1)

    >>> comb("","abcde", 3)
    abc
    abd
    abe
    acd
    ace
    ade
    bcd
    bce
    bde
    cde

    为了便于解释,下面的例子描述了递归方法:

    示例:A B C D E所有3种组合都是:

    • A,其余所有2种组合(b c d e)
    • b其余所有2种组合(c d e)
    • C,其余所有2种组合(d e)

    Haskell中的简单递归算法

    1
    2
    3
    4
    5
    6
    7
    import Data.List

    combinations 0 lst = [[]]
    combinations n lst = do
        (x:xs) <- tails lst
        rest   <- combinations (n-1) xs
        return $ x : rest

    我们首先定义特殊情况,即选择零元素。它生成一个单一的结果,即空列表(即包含空列表的列表)。

    对于n>0,x通过列表中的每个元素,xsx之后的每个元素。

    rest使用对combinations的递归调用从xs中选取n - 1个元素。函数的最终结果是一个列表,其中每个元素都是x : rest(即,对于xrest的每个不同值,都有x作为头,rest作为尾的列表)。

    1
    2
    > combinations 3"abcde"
    ["abc","abd","abe","acd","ace","ade","bcd","bce","bde","cde"]

    当然,由于haskell是懒惰的,列表会根据需要逐步生成,因此您可以部分地计算指数级大的组合。

    1
    2
    3
    4
    > let c = combinations 8"abcdefghijklmnopqrstuvwxyz"
    > take 10 c
    ["abcdefgh","abcdefgi","abcdefgj","abcdefgk","abcdefgl","abcdefgm","abcdefgn",
    "abcdefgo","abcdefgp","abcdefgq"]

    还有外公考博,这是一种非常恶毒的语言。

    让我们假设一个包含34个元素的数组,每个元素8个字节(纯粹是任意选择)。其思想是枚举所有可能的4元素组合,并将它们加载到一个数组中。

    我们使用4个指数,每组4个位置各一个。

    数组的处理方式如下:

    1
    2
    3
    4
        idx1 = 1
        idx2 = 2
        idx3 = 3
        idx4 = 4

    我们将IDX4从4改为4。对于每个IDX4,我们都有一个独特的组合四人一组。当IDX4到达数组末尾时,我们将IDX3增加1,并将IDX4设置为IDX3+1。然后我们再次运行IDX4。我们以这种方式继续,分别增加idx3、idx2和idx1,直到idx1的位置与数组末端的距离小于4。完成了算法。

    1
    2
    3
    4
    5
    6
    7
    8
    1          --- pos.1
    2          --- pos 2
    3          --- pos 3
    4          --- pos 4
    5
    6
    7
    etc.

    第一次迭代:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1234
    1235
    1236
    1237
    1245
    1246
    1247
    1256
    1257
    1267
    etc.

    COBOL示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    01  DATA_ARAY.
        05  FILLER     PIC X(8)    VALUE "VALUE_01".
        05  FILLER     PIC X(8)    VALUE "VALUE_02".
      etc.
    01  ARAY_DATA    OCCURS 34.
        05  ARAY_ITEM       PIC X(8).

    01  OUTPUT_ARAY   OCCURS  50000   PIC X(32).

    01   MAX_NUM   PIC 99 COMP VALUE 34.

    01  INDEXXES  COMP.
        05  IDX1            PIC 99.
        05  IDX2            PIC 99.
        05  IDX3            PIC 99.
        05  IDX4            PIC 99.
        05  OUT_IDX   PIC 9(9).

    01  WHERE_TO_STOP_SEARCH          PIC 99  COMP.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    * Stop the search when IDX1 is on the third last array element:

    COMPUTE WHERE_TO_STOP_SEARCH = MAX_VALUE - 3    

    MOVE 1 TO IDX1

    PERFORM UNTIL IDX1 > WHERE_TO_STOP_SEARCH
       COMPUTE IDX2 = IDX1 + 1
       PERFORM UNTIL IDX2 > MAX_NUM
          COMPUTE IDX3 = IDX2 + 1
          PERFORM UNTIL IDX3 > MAX_NUM
             COMPUTE IDX4 = IDX3 + 1
             PERFORM UNTIL IDX4 > MAX_NUM
                ADD 1 TO OUT_IDX
                STRING  ARAY_ITEM(IDX1)
                        ARAY_ITEM(IDX2)
                        ARAY_ITEM(IDX3)
                        ARAY_ITEM(IDX4)
                        INTO OUTPUT_ARAY(OUT_IDX)
                ADD 1 TO IDX4
             END-PERFORM
             ADD 1 TO IDX3
          END-PERFORM
          ADD 1 TO IDX2
       END_PERFORM
       ADD 1 TO IDX1
    END-PERFORM.


    如果您可以使用SQL语法-例如,如果您使用LINQ访问结构或数组的字段,或者直接访问只有一个char字段"letter"的表名为"alphabet"的数据库,则可以修改以下代码:

    1
    2
    3
    4
    SELECT A.Letter, B.Letter, C.Letter
    FROM Alphabet AS A, Alphabet AS B, Alphabet AS C
    WHERE A.Letter<>B.Letter AND A.Letter<>C.Letter AND B.Letter<>C.Letter
    AND A.Letter<B.Letter AND B.Letter<C.Letter

    这将返回3个字母的所有组合,不管"字母表"中有多少个字母(可以是3、8、10、27等)。

    如果您想要的是所有排列,而不是组合(即,您希望"acb"和"abc"计数为不同的,而不是仅仅出现一次),只需删除最后一行(和一行)即可完成。

    编辑后:重新阅读问题后,我意识到需要的是通用算法,而不仅仅是选择3个项目的具体算法。亚当休斯的回答是完整的,不幸的是我还不能投票决定。这个答案很简单,但只适用于你想要3个项目的时候。


    这里是scala中一个优雅的通用实现,如99个scala问题所描述的那样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    object P26 {
      def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] =
        ls match {
          case Nil => Nil
          case sublist@(_ :: tail) => f(sublist) ::: flatMapSublists(tail)(f)
        }

      def combinations[A](n: Int, ls: List[A]): List[List[A]] =
        if (n == 0) List(Nil)
        else flatMapSublists(ls) { sl =>
          combinations(n - 1, sl.tail) map {sl.head :: _}
        }
    }


    这里有一个用C编码的算法的延迟评估版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
        static bool nextCombination(int[] num, int n, int k)
        {
            bool finished, changed;

            changed = finished = false;

            if (k > 0)
            {
                for (int i = k - 1; !finished && !changed; i--)
                {
                    if (num[i] < (n - 1) - (k - 1) + i)
                    {
                        num[i]++;
                        if (i < k - 1)
                        {
                            for (int j = i + 1; j < k; j++)
                            {
                                num[j] = num[j - 1] + 1;
                            }
                        }
                        changed = true;
                    }
                    finished = (i == 0);
                }
            }

            return changed;
        }

        static IEnumerable Combinations<T>(IEnumerable<T> elements, int k)
        {
            T[] elem = elements.ToArray();
            int size = elem.Length;

            if (k <= size)
            {
                int[] numbers = new int[k];
                for (int i = 0; i < k; i++)
                {
                    numbers[i] = i;
                }

                do
                {
                    yield return numbers.Select(n => elem[n]);
                }
                while (nextCombination(numbers, size, k));
            }
        }

    测试部分:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        static void Main(string[] args)
        {
            int k = 3;
            var t = new[] {"dog","cat","mouse","zebra"};

            foreach (IEnumerable<string> i in Combinations(t, k))
            {
                Console.WriteLine(string.Join(",", i));
            }
        }

    希望这对你有帮助!


    另一个C版本,延迟生成组合索引。此版本维护一个索引数组,以定义所有值列表与当前组合值之间的映射,即在整个运行时持续使用O(K)额外空间。代码在O(k)时间内生成单个组合,包括第一个组合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public static IEnumerable<T[]> Combinations<T>(this T[] values, int k)
    {
        if (k < 0 || values.Length < k)
            yield break; // invalid parameters, no combinations possible

        // generate the initial combination indices
        var combIndices = new int[k];
        for (var i = 0; i < k; i++)
        {
            combIndices[i] = i;
        }

        while (true)
        {
            // return next combination
            var combination = new T[k];
            for (var i = 0; i < k; i++)
            {
                combination[i] = values[combIndices[i]];
            }
            yield return combination;

            // find first index to update
            var indexToUpdate = k - 1;
            while (indexToUpdate >= 0 && combIndices[indexToUpdate] >= values.Length - k + indexToUpdate)
            {
                indexToUpdate--;
            }

            if (indexToUpdate < 0)
                yield break; // done

            // update combination indices
            for (var combIndex = combIndices[indexToUpdate] + 1; indexToUpdate < k; indexToUpdate++, combIndex++)
            {
                combIndices[indexToUpdate] = combIndex;
            }
        }
    }

    测试代码:

    1
    2
    3
    4
    foreach (var combination in new[] {'a', 'b', 'c', 'd', 'e'}.Combinations(3))
    {
        System.Console.WriteLine(String.Join("", combination));
    }

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    a b c
    a b d
    a b e
    a c d
    a c e
    a d e
    b c d
    b c e
    b d e
    c d e


    我有一个用于Project Euler的置换算法,在python中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def missing(miss,src):
       "Returns the list of items in src not present in miss"
        return [i for i in src if i not in miss]


    def permutation_gen(n,l):
       "Generates all the permutations of n items of the l list"
        for i in l:
            if n<=1: yield [i]
            r = [i]
            for j in permutation_gen(n-1,missing([i],l)):  yield r+j

    如果

    1
    n<len(l)

    你应该拥有你需要的所有组合,而不是重复,你需要吗?

    它是一个发电机,所以你用它来做类似的事情:

    1
    2
    for comb in permutation_gen(3,list("ABCDEFGH")):
        print comb

    https://gist.github.com/3118596

    有一个JavaScript实现。它具有获取K组合和任意对象数组的所有组合的函数。实例:

    1
    2
    3
    4
    5
    k_combinations([1,2,3], 2)
    -> [[1,2], [1,3], [2,3]]

    combinations([1,2,3])
    -> [[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]

    Clojure版本:

    1
    2
    3
    4
    5
    6
    7
    (defn comb [k l]
      (if (= 1 k) (map vector l)
          (apply concat
                 (map-indexed
                  #(map (fn [x] (conj x %2))
                        (comb (dec k) (drop (inc %1) l)))
                  l))))

    假设您的字母数组如下所示:"abcdefgh"。你有三个索引(i,j,k),表示你要用哪个字母来表示当前单词,你从以下开始:

    1
    2
    3
    A B C D E F G H
    ^ ^ ^
    i j k

    首先你改变k,所以下一步看起来是这样的:

    1
    2
    3
    A B C D E F G H
    ^ ^   ^
    i j   k

    如果你到了终点,你继续改变j,然后再改变k。

    1
    2
    3
    4
    5
    6
    7
    A B C D E F G H
    ^   ^ ^
    i   j k

    A B C D E F G H
    ^   ^   ^
    i   j   k

    一旦你达到G,你也开始改变I。

    1
    2
    3
    4
    5
    6
    7
    8
    A B C D E F G H
      ^ ^ ^
      i j k

    A B C D E F G H
      ^ ^   ^
      i j   k
    ...
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    function initializePointers($cnt) {
        $pointers = [];

        for($i=0; $i<$cnt; $i++) {
            $pointers[] = $i;
        }

        return $pointers;    
    }

    function incrementPointers(&$pointers, &$arrLength) {
        for($i=0; $i<count($pointers); $i++) {
            $currentPointerIndex = count($pointers) - $i - 1;
            $currentPointer = $pointers[$currentPointerIndex];

            if($currentPointer < $arrLength - $i - 1) {
               ++$pointers[$currentPointerIndex];

               for($j=1; ($currentPointerIndex+$j)<count($pointers); $j++) {
                    $pointers[$currentPointerIndex+$j] = $pointers[$currentPointerIndex]+$j;
               }

               return true;
            }
        }

        return false;
    }

    function getDataByPointers(&$arr, &$pointers) {
        $data = [];

        for($i=0; $i<count($pointers); $i++) {
            $data[] = $arr[$pointers[$i]];
        }

        return $data;
    }

    function getCombinations($arr, $cnt)
    {
        $len = count($arr);
        $result = [];
        $pointers = initializePointers($cnt);

        do {
            $result[] = getDataByPointers($arr, $pointers);
        } while(incrementPointers($pointers, count($arr)));

        return $result;
    }

    $result = getCombinations([0, 1, 2, 3, 4, 5], 3);
    print_r($result);

    基于https://stackoverflow.com/a/127898/2628125,但对于任何大小的指针都更抽象。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    Array.prototype.combs = function(num) {

        var str = this,
            length = str.length,
            of = Math.pow(2, length) - 1,
            out, combinations = [];

        while(of) {

            out = [];

            for(var i = 0, y; i < length; i++) {

                y = (1 << i);

                if(y & of && (y !== of))
                    out.push(str[i]);

            }

            if (out.length >= num) {
               combinations.push(out);
            }

            of--;
        }

        return combinations;
    }

    所有的一切都说了又做了,这就是O'Caml代码。从代码中可以看出算法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let combi n lst =
        let rec comb l c =
            if( List.length c = n) then [c] else
            match l with
            [] -> []
            | (h::t) -> (combi t (h::c))@(combi t c)
        in
            combi lst []
    ;;

    这是我最近在Java中编写的一个代码,它计算并返回"NUM"元素与"Outof"元素的所有组合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    // author: Sourabh Bhat ([email protected])

    public class Testing
    {
        public static void main(String[] args)
        {

    // Test case num = 5, outOf = 8.

            int num = 5;
            int outOf = 8;
            int[][] combinations = getCombinations(num, outOf);
            for (int i = 0; i < combinations.length; i++)
            {
                for (int j = 0; j < combinations[i].length; j++)
                {
                    System.out.print(combinations[i][j] +"");
                }
                System.out.println();
            }
        }

        private static int[][] getCombinations(int num, int outOf)
        {
            int possibilities = get_nCr(outOf, num);
            int[][] combinations = new int[possibilities][num];
            int arrayPointer = 0;

            int[] counter = new int[num];

            for (int i = 0; i < num; i++)
            {
                counter[i] = i;
            }
            breakLoop: while (true)
            {
                // Initializing part
                for (int i = 1; i < num; i++)
                {
                    if (counter[i] >= outOf - (num - 1 - i))
                        counter[i] = counter[i - 1] + 1;
                }

                // Testing part
                for (int i = 0; i < num; i++)
                {
                    if (counter[i] < outOf)
                    {
                        continue;
                    } else
                    {
                        break breakLoop;
                    }
                }

                // Innermost part
                combinations[arrayPointer] = counter.clone();
                arrayPointer++;

                // Incrementing part
                counter[num - 1]++;
                for (int i = num - 1; i >= 1; i--)
                {
                    if (counter[i] >= outOf - (num - 1 - i))
                        counter[i - 1]++;
                }
            }

            return combinations;
        }

        private static int get_nCr(int n, int r)
        {
            if(r > n)
            {
                throw new ArithmeticException("r is greater then n");
            }
            long numerator = 1;
            long denominator = 1;
            for (int i = n; i >= r + 1; i--)
            {
                numerator *= i;
            }
            for (int i = 2; i <= n - r; i++)
            {
                denominator *= i;
            }

            return (int) (numerator / denominator);
        }
    }

    我在SQL Server 2005中为此创建了一个解决方案,并将其发布在我的网站上:http://www.jessemclain.com/downloads/code/sql/fn_etmchoosencombos.sql.htm

    下面是一个演示用法的示例:

    1
    SELECT * FROM dbo.fn_GetMChooseNCombos('ABCD', 2, '')

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Word
    ----
    AB
    AC
    AD
    BC
    BD
    CD

    (6 row(s) affected)


    下面是一个方法,它从一个随机长度的字符串中为您提供指定大小的所有组合。类似于Quinmars的解决方案,但适用于不同的输入和k。

    代码可以更改为环绕,即"dab"来自输入"abcd"w k=3。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public void run(String data, int howMany){
        choose(data, howMany, new StringBuffer(), 0);
    }


    //n choose k
    private void choose(String data, int k, StringBuffer result, int startIndex){
        if (result.length()==k){
            System.out.println(result.toString());
            return;
        }

        for (int i=startIndex; i<data.length(); i++){
            result.append(data.charAt(i));
            choose(data,k,result, i+1);
            result.setLength(result.length()-1);
        }
    }

    "abcde"的输出:

    abc abd abe acd ace ade bcd bce bde cde


    这是我在C++中的建议

    我试图尽可能少地对迭代器类型施加限制,所以这个解决方案假定只使用正向迭代器,它可以是一个常量迭代器。这应该适用于任何标准容器。在参数没有意义的情况下,它抛出std::invalidu参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    #include <vector>
    #include <stdexcept>

    template <typename Fci> // Fci - forward const iterator
    std::vector<std::vector<Fci> >
    enumerate_combinations(Fci begin, Fci end, unsigned int combination_size)
    {
        if(begin == end && combination_size > 0u)
            throw std::invalid_argument("empty set and positive combination size!");
        std::vector<std::vector<Fci> > result; // empty set of combinations
        if(combination_size == 0u) return result; // there is exactly one combination of
                                                  // size 0 - emty set
        std::vector<Fci> current_combination;
        current_combination.reserve(combination_size + 1u); // I reserve one aditional slot
                                                            // in my vector to store
                                                            // the end sentinel there.
                                                            // The code is cleaner thanks to that
        for(unsigned int i = 0u; i < combination_size && begin != end; ++i, ++begin)
        {
            current_combination.push_back(begin); // Construction of the first combination
        }
        // Since I assume the itarators support only incrementing, I have to iterate over
        // the set to get its size, which is expensive. Here I had to itrate anyway to  
        // produce the first cobination, so I use the loop to also check the size.
        if(current_combination.size() < combination_size)
            throw std::invalid_argument("combination size > set size!");
        result.push_back(current_combination); // Store the first combination in the results set
        current_combination.push_back(end); // Here I add mentioned earlier sentinel to
                                            // simplyfy rest of the code. If I did it
                                            // earlier, previous statement would get ugly.
        while(true)
        {
            unsigned int i = combination_size;
            Fci tmp;                            // Thanks to the sentinel I can find first
            do                                  // iterator to change, simply by scaning
            {                                   // from right to left and looking for the
                tmp = current_combination[--i]; // first"bubble". The fact, that it's
                ++tmp;                          // a forward iterator makes it ugly but I
            }                                   // can't help it.
            while(i > 0u && tmp == current_combination[i + 1u]);

            // Here is probably my most obfuscated expression.
            // Loop above looks for a"bubble". If there is no"bubble", that means, that
            // current_combination is the last combination, Expression in the if statement
            // below evaluates to true and the function exits returning result.
            // If the"bubble" is found however, the ststement below has a sideeffect of
            // incrementing the first iterator to the left of the"bubble".
            if(++current_combination[i] == current_combination[i + 1u])
                return result;
            // Rest of the code sets posiotons of the rest of the iterstors
            // (if there are any), that are to the right of the incremented one,
            // to form next combination

            while(++i < combination_size)
            {
                current_combination[i] = current_combination[i - 1u];
                ++current_combination[i];
            }
            // Below is the ugly side of using the sentinel. Well it had to haave some
            // disadvantage. Try without it.
            result.push_back(std::vector<Fci>(current_combination.begin(),
                                              current_combination.end() - 1));
        }
    }

    简洁的javascript解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Array.prototype.combine=function combine(k){    
        var toCombine=this;
        var last;
        function combi(n,comb){            
            var combs=[];
            for ( var x=0,y=comb.length;x<y;x++){
                for ( var l=0,m=toCombine.length;l<m;l++){      
                    combs.push(comb[x]+toCombine[l]);          
                }
            }
            if (n<k-1){
                n++;
                combi(n,combs);
            } else{last=combs;}
        }
        combi(1,toCombine);
        return last;
    }
    // Example:
    // var toCombine=['a','b','c'];
    // var results=toCombine.combine(4);

    算法:

    • 从1到2^n计数。
    • 将每个数字转换为二进制表示。
    • 根据位置将每个"on"位转换为集合中的元素。

    C中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    void Main()
    {
        var set = new [] {"A","B","C","D" }; //,"E","F","G","H","I","J" };

        var kElement = 2;

        for(var i = 1; i < Math.Pow(2, set.Length); i++) {
            var result = Convert.ToString(i, 2).PadLeft(set.Length, '0');
            var cnt = Regex.Matches(Regex.Escape(result), "1").Count;
            if (cnt == kElement) {
                for(int j = 0; j < set.Length; j++)
                    if ( Char.GetNumericValue(result[j]) == 1)
                        Console.Write(set[j]);
                Console.WriteLine();
            }
        }
    }

    为什么有效?

    N元素集的子集和N位序列之间存在双射。

    这意味着我们可以通过计算序列来计算出有多少个子集。

    例如,下面的四个元素集可以用0,1 x 0,1 x 0,1 x 0,1(或2^4)不同的序列表示。

    所以-我们要做的就是从1数到2^n来找到所有的组合。(我们忽略了空的集合。)接下来,将数字转换为二进制表示。然后用"on"位替换集合中的元素。

    如果只需要k元素结果,则仅在k位为"on"时打印。

    (如果希望所有子集而不是k长度子集,请删除cnt/kelement部分。)

    (有关证明,请参阅麻省理工学院计算机科学免费课件《数学》,雷曼等人,第11.2.2节。https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-fall-2010/readings/)


    我已经编写了一个处理二项式系数的公共函数的类,这是您的问题所属的问题类型。它执行以下任务:

  • 以很好的格式将所有k-索引输出给任意n个文件,选择k。k索引可以用更具描述性的字符串或字母代替。这种方法使得解决这类问题变得非常简单。

  • 将k-索引转换为排序二项式系数表中某项的正确索引。这项技术比以前发布的依赖迭代的技术快得多。它通过使用帕斯卡三角固有的数学特性来实现这一点。我的论文谈到这个。我相信我是第一个发现并发布这种技术的人,但我可能错了。

  • 将排序后的二项式系数表中的索引转换为相应的k索引。

  • 采用Mark-Domius方法计算二项系数,二项系数越小,越大的数值越容易溢出。

  • 该类是用.NET C编写的,并提供了一种使用通用列表管理与问题相关的对象(如果有)的方法。此类的构造函数接受一个名为inittable的bool值,如果为true,则将创建一个包含要管理的对象的泛型列表。如果该值为假,则不会创建表。为了执行上述4种方法,不需要创建表。提供了访问器方法来访问表。

  • 有一个关联的测试类,它显示如何使用该类及其方法。它已经过广泛的测试,有2个案例,没有已知的错误。

  • 要阅读关于这个类的内容并下载代码,请参见建立二项式系数。

    将这个类转换为C++并不困难。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void combine(char a[], int N, int M, int m, int start, char result[]) {
        if (0 == m) {
            for (int i = M - 1; i >= 0; i--)
                std::cout << result[i];
            std::cout << std::endl;
            return;
        }
        for (int i = start; i < (N - m + 1); i++) {
            result[m - 1] = a[i];
            combine(a, N, M, m-1, i+1, result);
        }
    }

    void combine(char a[], int N, int M) {
        char *result = new char[M];
        combine(a, N, M, M, 0, result);
        delete[] result;
    }

    在第一个函数中,m表示还需要选择多少,start表示必须从数组中的哪个位置开始选择。


    lisp宏为所有值r生成代码(每次执行)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    (defmacro txaat (some-list taken-at-a-time)
      (let* ((vars (reverse (truncate-list '(a b c d e f g h i j) taken-at-a-time))))
        `(
          ,@(loop for i below taken-at-a-time
               for j in vars
               with nested = nil
               finally (return nested)
               do
                 (setf
                  nested
                  `(loop for ,j from
                        ,(if (< i (1- (length vars)))
                             `(1+ ,(nth (1+ i) vars))
                             0)
                      below (- (length ,some-list) ,i)
                        ,@(if (equal i 0)
                              `(collect
                                   (list
                                    ,@(loop for k from (1- taken-at-a-time) downto 0
                                         append `((nth ,(nth k vars) ,some-list)))))
                              `(append ,nested))))))))

    所以,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    CL-USER> (macroexpand-1 '(txaat '(a b c d) 1))
    (LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 1)
        COLLECT (LIST (NTH A '(A B C D))))
    T
    CL-USER> (macroexpand-1 '(txaat '(a b c d) 2))
    (LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 2)
          APPEND (LOOP FOR B FROM (1+ A) TO (- (LENGTH '(A B C D)) 1)
                       COLLECT (LIST (NTH A '(A B C D)) (NTH B '(A B C D)))))
    T
    CL-USER> (macroexpand-1 '(txaat '(a b c d) 3))
    (LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 3)
          APPEND (LOOP FOR B FROM (1+ A) TO (- (LENGTH '(A B C D)) 2)
                       APPEND (LOOP FOR C FROM (1+ B) TO (- (LENGTH '(A B C D)) 1)
                                    COLLECT (LIST (NTH A '(A B C D))
                                                  (NTH B '(A B C D))
                                                  (NTH C '(A B C D))))))
    T

    CL-USER>

    而且,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CL-USER> (txaat '(a b c d) 1)
    ((A) (B) (C) (D))
    CL-USER> (txaat '(a b c d) 2)
    ((A B) (A C) (A D) (B C) (B D) (C D))
    CL-USER> (txaat '(a b c d) 3)
    ((A B C) (A B D) (A C D) (B C D))
    CL-USER> (txaat '(a b c d) 4)
    ((A B C D))
    CL-USER> (txaat '(a b c d) 5)
    NIL
    CL-USER> (txaat '(a b c d) 0)
    NIL
    CL-USER>

    在vb.net中,该算法从一组数字(poolarray)中收集n个数字的所有组合。例如,"8,10,20,33,41,44,47"中5个选择的所有组合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Sub CreateAllCombinationsOfPicksFromPool(ByVal PicksArray() As UInteger, ByVal PicksIndex As UInteger, ByVal PoolArray() As UInteger, ByVal PoolIndex As UInteger)
        If PicksIndex < PicksArray.Length Then
            For i As Integer = PoolIndex To PoolArray.Length - PicksArray.Length + PicksIndex
                PicksArray(PicksIndex) = PoolArray(i)
                CreateAllCombinationsOfPicksFromPool(PicksArray, PicksIndex + 1, PoolArray, i + 1)
            Next
        Else
            ' completed combination. build your collections using PicksArray.
        End If
    End Sub

            Dim PoolArray() As UInteger = Array.ConvertAll("8,10,20,33,41,44,47".Split(","), Function(u) UInteger.Parse(u))
            Dim nPicks as UInteger = 5
            Dim Picks(nPicks - 1) As UInteger
            CreateAllCombinationsOfPicksFromPool(Picks, 0, PoolArray, 0)

    下面是一些打印所有C(N,M)组合的简单代码。它通过初始化和移动一组指向下一个有效组合的数组索引来工作。这些索引被初始化为指向最低的m索引(在词典上是最小的组合)。然后,从第m个指数开始,我们尝试向前移动这些指数。如果某个索引已达到其限制,我们将尝试上一个索引(一直到索引1)。如果我们可以向前移动一个索引,那么我们会重置所有更大的索引。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    m=(rand()%n)+1; // m will vary from 1 to n

    for (i=0;i<n;i++) a[i]=i+1;

    // we want to print all possible C(n,m) combinations of selecting m objects out of n
    printf("Printing C(%d,%d) possible combinations ...
    ", n,m);

    // This is an adhoc algo that keeps m pointers to the next valid combination
    for (i=0;i<m;i++) p[i]=i; // the p[.] contain indices to the a vector whose elements constitute next combination

    done=false;
    while (!done)
    {
        // print combination
        for (i=0;i<m;i++) printf("%2d", a[p[i]]);
        printf("
    ");

        // update combination
        // method: start with p[m-1]. try to increment it. if it is already at the end, then try moving p[m-2] ahead.
        // if this is possible, then reset p[m-1] to 1 more than (the new) p[m-2].
        // if p[m-2] can not also be moved, then try p[m-3]. move that ahead. then reset p[m-2] and p[m-1].
        // repeat all the way down to p[0]. if p[0] can not also be moved, then we have generated all combinations.
        j=m-1;
        i=1;
        move_found=false;
        while ((j>=0) && !move_found)
        {
            if (p[j]<(n-i))
            {
                move_found=true;
                p[j]++; // point p[j] to next index
                for (k=j+1;k<m;k++)
                {
                    p[k]=p[j]+(k-j);
                }
            }
            else
            {
                j--;
                i++;
            }
        }
        if (!move_found) done=true;
    }

    这是一个递归程序,它为集合中的nCk.Elements生成组合,假定是从1n

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include<stdio.h>
    #include<stdlib.h>

    int nCk(int n,int loopno,int ini,int *a,int k)
    {
        static int count=0;
        int i;
        loopno--;
        if(loopno<0)
        {
            a[k-1]=ini;
            for(i=0;i<k;i++)
            {
                printf("%d,",a[i]);
            }
            printf("
    ");
            count++;
            return 0;
        }
        for(i=ini;i<=n-loopno-1;i++)
        {
            a[k-1-loopno]=i+1;
            nCk(n,loopno,i+1,a,k);
        }
        if(ini==0)
        return count;
        else
        return 0;
    }

    void main()
    {
        int n,k,*a,count;
        printf("Enter the value of n and k
    ");
        scanf("%d %d",&n,&k);
        a=(int*)malloc(k*sizeof(int));
        count=nCk(n,k,0,a,k);
        printf("No of combinations=%d
    ",count);
    }


    由于没有提到编程语言,我假设列表也可以。所以这里有一个适合短列表(非尾部递归)的OCAML版本。给定任意类型元素的列表l和整数n,如果我们假定结果列表中元素的顺序被忽略,即列表['a';'b']与['b';'a']相同,并将报告一次,则它将返回包含n个元素的所有可能列表的列表。所以结果列表的大小将是((list.length l)选择n)。

    递归的直觉如下:取列表的头,然后进行两个递归调用:

    • 递归调用1(rc1):指向列表的尾部,但选择n-1元素
    • 递归调用2(rc2):指向列表的尾部,但选择n个元素

    要组合递归结果,请将列表的头与rc1的结果相乘(请使用奇数名称),然后将rc2的结果附加(@)。list multiple是以下操作lmul

    1
    a lmul [ l1 ; l2 ; l3] = [a::l1 ; a::l2 ; a::l3]

    lmul在下面的代码中实现为

    1
    List.map (fun x -> h::x)

    当列表的大小等于要选择的元素数时,递归将终止,在这种情况下,您只需返回列表本身。

    因此,OCAML中有一个实现上述算法的四行程序:

    1
    2
    3
    4
        let rec choose l n = match l, (List.length l) with                                
        | _, lsize  when n==lsize  -> [l]                                
        | h::t, _ -> (List.map (fun x-> h::x) (choose t (n-1))) @ (choose t n)  
        | [], _ -> []

    跳上潮流,发布另一个解决方案。这是一个通用的Java实现。输入:(int k)为要选择的元素数,(List list)为要选择的列表。返回组合(List>)的列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public static <T> List<List<T>> getCombinations(int k, List<T> list) {
        List<List<T>> combinations = new ArrayList<List<T>>();
        if (k == 0) {
            combinations.add(new ArrayList<T>());
            return combinations;
        }
        for (int i = 0; i < list.size(); i++) {
            T element = list.get(i);
            List<T> rest = getSublist(list, i+1);
            for (List<T> previous : getCombinations(k-1, rest)) {
                previous.add(element);
                combinations.add(previous);
            }
        }
        return combinations;
    }

    public static <T> List<T> getSublist(List<T> list, int i) {
        List<T> sublist = new ArrayList<T>();
        for (int j = i; j < list.size(); j++) {
            sublist.add(list.get(j));
        }
        return sublist;
    }

    这里有一个clojure版本,使用我在OCAML实现答案中描述的相同算法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    (defn select
      ([items]
         (select items 0 (inc (count items))))
      ([items n1 n2]
         (reduce concat
                 (map #(select % items)
                      (range n1 (inc n2)))))
      ([n items]
         (let [
               lmul (fn [a list-of-lists-of-bs]
                         (map #(cons a %) list-of-lists-of-bs))
               ]
           (if (= n (count items))
             (list items)
             (if (empty? items)
               items
               (concat
                (select n (rest items))
                (lmul (first items) (select (dec n) (rest items)))))))))

    它提供了三种调用方法:

    (a)对于问题所需的N个选定项目:

    1
    2
      user=> (count (select 3"abcdefgh"))
      56

    (b)对于N1和N2之间的选定项目:

    1
    2
    user=> (select '(1 2 3 4) 2 3)
    ((3 4) (2 4) (2 3) (1 4) (1 3) (1 2) (2 3 4) (1 3 4) (1 2 4) (1 2 3))

    (c)对于介于0和集合选定项的大小之间的值:

    1
    2
    user=> (select '(1 2 3))
    (() (3) (2) (1) (2 3) (1 3) (1 2) (1 2 3))

    基于Java解决方案的K元素从N(二项系数)返回所有组合的短PHP算法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    $array = array(1,2,3,4,5);

    $array_result = NULL;

    $array_general = NULL;

    function combinations($array, $len, $start_position, $result_array, $result_len, &$general_array)
    {
        if($len == 0)
        {
            $general_array[] = $result_array;
            return;
        }

        for ($i = $start_position; $i <= count($array) - $len; $i++)
        {
            $result_array[$result_len - $len] = $array[$i];
            combinations($array, $len-1, $i+1, $result_array, $result_len, $general_array);
        }
    }

    combinations($array, 3, 0, $array_result, 3, $array_general);

    echo"[cc]";
    print_r($array_general);
    echo"

    ;< /代码>

    相同的解决方案,但在javascript中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var newArray = [1, 2, 3, 4, 5];
    var arrayResult = [];
    var arrayGeneral = [];

    function combinations(newArray, len, startPosition, resultArray, resultLen, arrayGeneral) {
        if(len === 0) {
            var tempArray = [];
            resultArray.forEach(value => tempArray.push(value));
            arrayGeneral.push(tempArray);
            return;
        }
        for (var i = startPosition; i <= newArray.length - len; i++) {
            resultArray[resultLen - len] = newArray[i];
            combinations(newArray, len-1, i+1, resultArray, resultLen, arrayGeneral);
        }
    }

    combinations(newArray, 3, 0, arrayResult, 3, arrayGeneral);

    console.log(arrayGeneral);


    另一个解决方案是C:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     static List<List<T>> GetCombinations<T>(List<T> originalItems, int combinationLength)
        {
            if (combinationLength < 1)
            {
                return null;
            }

            return CreateCombinations<T>(new List<T>(), 0, combinationLength, originalItems);
        }

     static List<List<T>> CreateCombinations<T>(List<T> initialCombination, int startIndex, int length, List<T> originalItems)
        {
            List<List<T>> combinations = new List<List<T>>();
            for (int i = startIndex; i < originalItems.Count - length + 1; i++)
            {
                List<T> newCombination = new List<T>(initialCombination);
                newCombination.Add(originalItems[i]);
                if (length > 1)
                {
                    List<List<T>> newCombinations = CreateCombinations(newCombination, i + 1, length - 1, originalItems);
                    combinations.AddRange(newCombinations);
                }
                else
                {
                    combinations.Add(newCombination);
                }
            }

            return combinations;
        }

    使用示例:

    1
    2
    3
       List<char> initialArray = new List<char>() { 'a','b','c','d'};
       int combinationLength = 3;
       List<List<char>> combinations = GetCombinations(initialArray, combinationLength);

    这是我的scala解决方案:

    1
    2
    3
    4
    def combinations[A](s: List[A], k: Int): List[List[A]] =
      if (k > s.length) Nil
      else if (k == 1) s.map(List(_))
      else combinations(s.tail, k - 1).map(s.head :: _) ::: combinations(s.tail, k)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    #include <stdio.h>

    unsigned int next_combination(unsigned int *ar, size_t n, unsigned int k)
    {
        unsigned int finished = 0;
        unsigned int changed = 0;
        unsigned int i;

        if (k > 0) {
            for (i = k - 1; !finished && !changed; i--) {
                if (ar[i] < (n - 1) - (k - 1) + i) {
                    /* Increment this element */
                    ar[i]++;
                    if (i < k - 1) {
                        /* Turn the elements after it into a linear sequence */
                        unsigned int j;
                        for (j = i + 1; j < k; j++) {
                            ar[j] = ar[j - 1] + 1;
                        }
                    }
                    changed = 1;
                }
                finished = i == 0;
            }
            if (!changed) {
                /* Reset to first combination */
                for (i = 0; i < k; i++) {
                    ar[i] = i;
                }
            }
        }
        return changed;
    }

    typedef void(*printfn)(const void *, FILE *);

    void print_set(const unsigned int *ar, size_t len, const void **elements,
        const char *brackets, printfn print, FILE *fptr)
    {
        unsigned int i;
        fputc(brackets[0], fptr);
        for (i = 0; i < len; i++) {
            print(elements[ar[i]], fptr);
            if (i < len - 1) {
                fputs(",", fptr);
            }
        }
        fputc(brackets[1], fptr);
    }

    int main(void)
    {
        unsigned int numbers[] = { 0, 1, 2 };
        char *elements[] = {"a","b","c","d","e" };
        const unsigned int k = sizeof(numbers) / sizeof(unsigned int);
        const unsigned int n = sizeof(elements) / sizeof(const char*);

        do {
            print_set(numbers, k, (void*)elements,"[]", (printfn)fputs, stdout);
            putchar('
    ');
        } while (next_combination(numbers, n, k));
        getchar();
        return 0;
    }

    短python代码,生成索引位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def yield_combos(n,k):
        # n is set size, k is combo size

        i = 0
        a = [0 for i in range(k)]

        while i > -1:
            for j in range(i+1, k):
                a[j] = a[j-1]+1
            i=j
            yield a
            while a[i] == i + n - k:
                i -= 1
            a[i] += 1

    我在为PHP寻找类似的解决方案,遇到了以下问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    class Combinations implements Iterator
    {
        protected $c = null;
        protected $s = null;
        protected $n = 0;
        protected $k = 0;
        protected $pos = 0;

        function __construct($s, $k) {
            if(is_array($s)) {
                $this->s = array_values($s);
                $this->n = count($this->s);
            } else {
                $this->s = (string) $s;
                $this->n = strlen($this->s);
            }
            $this->k = $k;
            $this->rewind();
        }
        function key() {
            return $this->pos;
        }
        function current() {
            $r = array();
            for($i = 0; $i < $this->k; $i++)
                $r[] = $this->s[$this->c[$i]];
            return is_array($this->s) ? $r : implode('', $r);
        }
        function next() {
            if($this->_next())
                $this->pos++;
            else
                $this->pos = -1;
        }
        function rewind() {
            $this->c = range(0, $this->k);
            $this->pos = 0;
        }
        function valid() {
            return $this->pos >= 0;
        }

        protected function _next() {
            $i = $this->k - 1;
            while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
                $i--;
            if($i < 0)
                return false;
            $this->c[$i]++;
            while($i++ < $this->k - 1)
                $this->c[$i] = $this->c[$i - 1] + 1;
            return true;
        }
    }


    foreach(new Combinations("1234567", 5) as $substring)
        echo $substring, ' ';

    来源

    我不确定这门课有多高效,但我只是把它用在播种机上。


    《计算机编程技术》第4卷第7.2.1.3节"组合算法"第1部分"算法L"(词典组合)的C代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <stdio.h>
    #include <stdlib.h>

    void visit(int* c, int t)
    {
      // for (int j = 1; j <= t; j++)
      for (int j = t; j > 0; j--)
        printf("%d", c[j]);
      printf("
    ");
    }

    int* initialize(int n, int t)
    {
      // c[0] not used
      int *c = (int*) malloc((t + 3) * sizeof(int));

      for (int j = 1; j <= t; j++)
        c[j] = j - 1;
      c[t+1] = n;
      c[t+2] = 0;
      return c;
    }

    void comb(int n, int t)
    {
      int *c = initialize(n, t);
      int j;

      for (;;) {
        visit(c, t);
        j = 1;
        while (c[j]+1 == c[j+1]) {
          c[j] = j - 1;
          ++j;
        }
        if (j > t)
          return;
        ++c[j];
      }
      free(c);
    }

    int main(int argc, char *argv[])
    {
      comb(5, 3);
      return 0;
    }


    在Python中像安德里亚一样埋伏,但没有硬编码选择三个。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    def combinations(list, k):
       """Choose combinations of list, choosing k elements(no repeats)"""
        if len(list) < k:
            return []
        else:
            seq = [i for i in range(k)]
            while seq:
                print [list[index] for index in seq]
                seq = get_next_combination(len(list), k, seq)

    def get_next_combination(num_elements, k, seq):
            index_to_move = find_index_to_move(num_elements, seq)
            if index_to_move == None:
                return None
            else:
                seq[index_to_move] += 1

                #for every element past this sequence, move it down
                for i, elem in enumerate(seq[(index_to_move+1):]):
                    seq[i + 1 + index_to_move] = seq[index_to_move] + i + 1

                return seq

    def find_index_to_move(num_elements, seq):
           """Tells which index should be moved"""
            for rev_index, elem in enumerate(reversed(seq)):
                if elem < (num_elements - rev_index - 1):
                    return len(seq) - rev_index - 1
            return None

    递归地,一个非常简单的答案,combo,在自由帕斯卡。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
        procedure combinata (n, k :integer; producer :oneintproc);

            procedure combo (ndx, nbr, len, lnd :integer);
            begin
                for nbr := nbr to len do begin
                    productarray[ndx] := nbr;
                    if len < lnd then
                        combo(ndx+1,nbr+1,len+1,lnd)
                    else
                        producer(k);
                end;
            end;

        begin
            combo (0, 0, n-k, n-1);
        end;

    "Producer"处理为每个组合制作的ProductArray。


    不需要集合操作。这个问题几乎与在K嵌套循环上循环一样,但是必须注意索引和边界(忽略Java和OOP内容):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     public class CombinationsGen {
        private final int n;
        private final int k;
        private int[] buf;

        public CombinationsGen(int n, int k) {
            this.n = n;
            this.k = k;
        }

        public void combine(Consumer<int[]> consumer) {
            buf = new int[k];
            rec(0, 0, consumer);
        }

        private void rec(int index, int next, Consumer<int[]> consumer) {
            int max = n - index;

            if (index == k - 1) {
                for (int i = 0; i < max && next < n; i++) {
                    buf[index] = next;
                    next++;
                    consumer.accept(buf);
                }
            } else {
                for (int i = 0; i < max && next + index < n; i++) {
                    buf[index] = next;
                    next++;
                    rec(index + 1, next, consumer);
                }
            }
        }
    }

    使用如下:

    1
    2
    3
    4
    5
    6
    7
    8
     CombinationsGen gen = new CombinationsGen(5, 2);

     AtomicInteger total = new AtomicInteger();
     gen.combine(arr -> {
         System.out.println(Arrays.toString(arr));
         total.incrementAndGet();
     });
     System.out.println(total);

    获取预期结果:

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

    最后,将索引映射到您可能拥有的任何数据集。


    简单但速度慢的C++回溯算法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include <iostream>

    void backtrack(int* numbers, int n, int k, int i, int s)
    {
        if (i == k)
        {
            for (int j = 0; j < k; ++j)
            {
                std::cout << numbers[j];
            }
            std::cout << std::endl;

            return;
        }

        if (s > n)
        {
            return;
        }

        numbers[i] = s;
        backtrack(numbers, n, k, i + 1, s + 1);
        backtrack(numbers, n, k, i, s + 1);
    }

    int main(int argc, char* argv[])
    {
        int n = 5;
        int k = 3;

        int* numbers = new int[k];

        backtrack(numbers, n, k, 0, 1);

        delete[] numbers;

        return 0;
    }

    我们可以使用位的概念来实现这一点。让我们有一个字符串"a bc",我们希望有长度为2的所有元素组合(即"ab"、"ac"、"bc")。

    我们可以在1到2^n(不含)的数字中找到集合位。这里是1到7,只要我们设置了bits=2,就可以从字符串中打印相应的值。

    例如:

    • 1—001
    • 2—010
    • 3-011->print ab (str[0] , str[1])
    • 4—100
    • 5-101->print ac (str[0] , str[2])
    • 6-110->print ab (str[1] , str[2])
    • 7—111。

    < BR>代码示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public class StringCombinationK {  
        static void combk(String s , int k){
            int n = s.length();
            int num = 1<<n;
            int j=0;
            int count=0;

            for(int i=0;i<num;i++){
                if (countSet(i)==k){
                    setBits(i,j,s);
                    count++;
                    System.out.println();
                }
            }

            System.out.println(count);
        }

        static void setBits(int i,int j,String s){ // print the corresponding string value,j represent the index of set bit
            if(i==0){
                return;
            }

            if(i%2==1){
                System.out.print(s.charAt(j));                  
            }

            setBits(i/2,j+1,s);
        }

        static int countSet(int i){ //count number of set bits
            if( i==0){
                return 0;
            }

            return (i%2==0? 0:1) + countSet(i/2);
        }

        public static void main(String[] arhs){
            String s ="abcdefgh";
            int k=3;
            combk(s,k);
        }
    }

    我为C++中的组合做了一个普通的类。它是这样使用的。

    1
    2
    3
    4
    5
    6
    char ar[] ="0ABCDEFGH";
    nCr ncr(8, 3);
    while(ncr.next()) {
        for(int i=0; i<ncr.size(); i++) cout << ar[ncr[i]];
        cout << ' ';
    }

    我的库ncr[i]从1返回,而不是从0返回。这就是数组中有0的原因。如果你想考虑订单,只需将NCR类更改为NPR。用法相同。

    结果

    基础知识脱落酸安倍ABF阿布格ABHACD王牌ACFACG胆碱阿德ADF自动增益控制腺苷脱氢酶原子能基金会AEGAEHAFGAFHAGHBCDBCEBCF卡介苗BCHBDEBDFBDGBDHBEF乞讨贝赫高炉煤气BFH牛生长激素化学需氧量彩色多普勒血流CDGCDH头孢噻吩CEGCEHCFGCFH计算全息DEFDEGDEH分布式光纤光栅二氢氟化氢生长激素EFGEFH表皮生长因子生长激素释放激素

    这是头文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    #pragma once
    #include <exception>

    class NRexception : public std::exception
    {
    public:
        virtual const char* what() const throw() {
            return"Combination : N, R should be positive integer!!";
        }
    };

    class Combination
    {
    public:
        Combination(int n, int r);
        virtual ~Combination() { delete [] ar;}
        int& operator[](unsigned i) {return ar[i];}
        bool next();
        int size() {return r;}
        static int factorial(int n);

    protected:
        int* ar;
        int n, r;
    };

    class nCr : public Combination
    {
    public:
        nCr(int n, int r);
        bool next();
        int count() const;
    };

    class nTr : public Combination
    {
    public:
        nTr(int n, int r);
        bool next();
        int count() const;
    };

    class nHr : public nTr
    {
    public:
        nHr(int n, int r) : nTr(n,r) {}
        bool next();
        int count() const;
    };

    class nPr : public Combination
    {
    public:
        nPr(int n, int r);
        virtual ~nPr() {delete [] on;}
        bool next();
        void rewind();
        int count() const;

    private:
        bool* on;
        void inc_ar(int i);
    };

    以及实施。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    #include"combi.h"
    #include <set>
    #include<cmath>

    Combination::Combination(int n, int r)
    {
        //if(n < 1 || r < 1) throw NRexception();
        ar = new int[r];
        this->n = n;
        this->r = r;
    }

    int Combination::factorial(int n)
    {
        return n == 1 ? n : n * factorial(n-1);
    }

    int nPr::count() const
    {
        return factorial(n)/factorial(n-r);
    }

    int nCr::count() const
    {
        return factorial(n)/factorial(n-r)/factorial(r);
    }

    int nTr::count() const
    {
        return pow(n, r);
    }

    int nHr::count() const
    {
        return factorial(n+r-1)/factorial(n-1)/factorial(r);
    }

    nCr::nCr(int n, int r) : Combination(n, r)
    {
        if(r == 0) return;
        for(int i=0; i<r-1; i++) ar[i] = i + 1;
        ar[r-1] = r-1;
    }

    nTr::nTr(int n, int r) : Combination(n, r)
    {
        for(int i=0; i<r-1; i++) ar[i] = 1;
        ar[r-1] = 0;
    }

    bool nCr::next()
    {
        if(r == 0) return false;
        ar[r-1]++;
        int i = r-1;
        while(ar[i] == n-r+2+i) {
            if(--i == -1) return false;
            ar[i]++;
        }
        while(i < r-1) ar[i+1] = ar[i++] + 1;
        return true;
    }

    bool nTr::next()
    {
        ar[r-1]++;
        int i = r-1;
        while(ar[i] == n+1) {
            ar[i] = 1;
            if(--i == -1) return false;
            ar[i]++;
        }
        return true;
    }

    bool nHr::next()
    {
        ar[r-1]++;
        int i = r-1;
        while(ar[i] == n+1) {
            if(--i == -1) return false;
            ar[i]++;
        }
        while(i < r-1) ar[i+1] = ar[i++];
        return true;
    }

    nPr::nPr(int n, int r) : Combination(n, r)
    {
        on = new bool[n+2];
        for(int i=0; i<n+2; i++) on[i] = false;
        for(int i=0; i<r; i++) {
            ar[i] = i + 1;
            on[i] = true;
        }
        ar[r-1] = 0;
    }

    void nPr::rewind()
    {
        for(int i=0; i<r; i++) {
            ar[i] = i + 1;
            on[i] = true;
        }
        ar[r-1] = 0;
    }

    bool nPr::next()
    {  
        inc_ar(r-1);

        int i = r-1;
        while(ar[i] == n+1) {
            if(--i == -1) return false;
            inc_ar(i);
        }
        while(i < r-1) {
            ar[++i] = 0;
            inc_ar(i);
        }
        return true;
    }

    void nPr::inc_ar(int i)
    {
        on[ar[i]] = false;
        while(on[++ar[i]]);
        if(ar[i] != n+1) on[ar[i]] = true;
    }

    作为迭代器对象实现的metatrader MQL4的快速组合。

    代码很容易理解。

    我对很多算法进行了基准测试,这一个非常快——比大多数下一个_Combination()函数快3倍。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    class CombinationsIterator
    {
    private:
        int input_array[];  // 1 2 3 4 5
        int index_array[];  // i j k
        int m_elements;     // N
        int m_indices;      // K

    public:
        CombinationsIterator(int &src_data[], int k)
        {
            m_indices = k;
            m_elements = ArraySize(src_data);
            ArrayCopy(input_array, src_data);
            ArrayResize(index_array, m_indices);

            // create initial combination (0..k-1)
            for (int i = 0; i < m_indices; i++)
            {
                index_array[i] = i;
            }
        }

        // https://stackoverflow.com/questions/5076695
        // bool next_combination(int &item[], int k, int N)
        bool advance()
        {
            int N = m_elements;
            for (int i = m_indices - 1; i >= 0; --i)
            {
                if (index_array[i] < --N)
                {
                    ++index_array[i];
                    for (int j = i + 1; j < m_indices; ++j)
                    {
                        index_array[j] = index_array[j - 1] + 1;
                    }
                    return true;
                }
            }
            return false;
        }

        void getItems(int &items[])
        {
            // fill items[] from input array
            for (int i = 0; i < m_indices; i++)
            {
                items[i] = input_array[index_array[i]];
            }
        }
    };

    用于测试上述迭代器类的驱动程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    // driver program to test above class

    #define N 5
    #define K 3

    void OnStart()
    {
        int myset[N] = {1, 2, 3, 4, 5};
        int items[K];

        CombinationsIterator comboIt(myset, K);

        do
        {
            comboIt.getItems(items);

            printf("%s", ArrayToString(items));

        } while (comboIt.advance());

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Output:
    1 2 3
    1 2 4
    1 2 5
    1 3 4
    1 3 5
    1 4 5
    2 3 4
    2 3 5
    2 4 5
    3 4 5


    下面是一个简单的JS解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    function getAllCombinations(n, k, f1) {
        indexes = Array(k);
      for (let i =0; i< k; i++) {
        indexes[i] = i;
      }
      var total = 1;
      f1(indexes);
      while (indexes[0] !== n-k) {
        total++;
            getNext(n, indexes);
        f1(indexes);
      }
      return {total};
    }

    function getNext(n, vec) {
        const k = vec.length;
      vec[k-1]++;
        for (var i=0; i<k; i++) {
        var currentIndex = k-i-1;
        if (vec[currentIndex] === n - i) {
            var nextIndex = k-i-2;
          vec[nextIndex]++;
          vec[currentIndex] = vec[nextIndex] + 1;
        }
      }

        for (var i=1; i<k; i++) {
        if (vec[i] === n - (k-i - 1)) {
          vec[i] = vec[i-1] + 1;
        }
      }
        return vec;
    }



    let start = new Date();
    let result = getAllCombinations(10, 3, indexes => console.log(indexes));
    let runTime = new Date() - start;

    console.log({
    result, runTime
    });


    我想提出我的解决方案。在next中没有递归调用,也没有嵌套循环。代码的核心是next()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public class Combinations {
        final int pos[];
        final List<Object> set;

        public Combinations(List<?> l, int k) {
            pos = new int[k];
            set=new ArrayList<Object>(l);
            reset();
        }
        public void reset() {
            for (int i=0; i < pos.length; ++i) pos[i]=i;
        }
        public boolean next() {
            int i = pos.length-1;
            for (int maxpos = set.size()-1; pos[i] >= maxpos; --maxpos) {
                if (i==0) return false;
                --i;
            }
            ++pos[i];
            while (++i < pos.length)
                pos[i]=pos[i-1]+1;
            return true;
        }

        public void getSelection(List<?> l) {
            @SuppressWarnings("unchecked")
            List<Object> ll = (List<Object>)l;
            if (ll.size()!=pos.length) {
                ll.clear();
                for (int i=0; i < pos.length; ++i)
                    ll.add(set.get(pos[i]));
            }
            else {
                for (int i=0; i < pos.length; ++i)
                    ll.set(i, set.get(pos[i]));
            }
        }
    }

    使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    static void main(String[] args) {
        List<Character> l = new ArrayList<Character>();
        for (int i=0; i < 32; ++i) l.add((char)('a'+i));
        Combinations comb = new Combinations(l,5);
        int n=0;
        do {
            ++n;
            comb.getSelection(l);
            //Log.debug("%d: %s", n, l.toString());
        } while (comb.next());
        Log.debug("num = %d", n);
    }

    短快速C实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>

    void main(int argc, char *argv[]) {
      const int n = 6; /* The size of the set; for {1, 2, 3, 4} it's 4 */
      const int p = 4; /* The size of the subsets; for {1, 2}, {1, 3}, ... it's 2 */
      int comb[40] = {0}; /* comb[i] is the index of the i-th element in the combination */

      int i = 0;
      for (int j = 0; j <= n; j++) comb[j] = 0;
      while (i >= 0) {
        if (comb[i] < n + i - p + 1) {
           comb[i]++;
           if (i == p - 1) { for (int j = 0; j < p; j++) printf("%d", comb[j]); printf("
    "); }
           else            { comb[++i] = comb[i - 1]; }
        } else i--; }
    }

    要查看它有多快,请使用此代码并测试它

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <time.h>
    #include <stdio.h>

    void main(int argc, char *argv[]) {
      const int n = 32; /* The size of the set; for {1, 2, 3, 4} it's 4 */
      const int p = 16; /* The size of the subsets; for {1, 2}, {1, 3}, ... it's 2 */
      int comb[40] = {0}; /* comb[i] is the index of the i-th element in the combination */

      int c = 0; int i = 0;
      for (int j = 0; j <= n; j++) comb[j] = 0;
      while (i >= 0) {
        if (comb[i] < n + i - p + 1) {
           comb[i]++;
           /* if (i == p - 1) { for (int j = 0; j < p; j++) printf("%d", comb[j]); printf("
    "); } */
           if (i == p - 1) c++;
           else            { comb[++i] = comb[i - 1]; }
        } else i--; }
      printf("%d!%d == %d combination(s) in %15.3f second(s)
    ", p, n, c, clock()/1000.0);
    }

    用cmd.exe(Windows)测试:

    1
    2
    3
    4
    5
    6
    7
    Microsoft Windows XP [Version 5.1.2600]
    (C) Copyright 1985-2001 Microsoft Corp.

    c:\Program Files\lcc\projects>combination
    16!32 == 601080390 combination(s) in          5.781 second(s)

    c:\Program Files\lcc\projects>

    祝你今天愉快。


    这是我提出的解决这个问题的算法。它以C++编写,但可以适用于支持逐位操作的任何语言。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void r_nCr(const unsigned int &startNum, const unsigned int &bitVal, const unsigned int &testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
    {
        unsigned int n = (startNum - bitVal) << 1;
        n += bitVal ? 1 : 0;

        for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
            cout << (n >> (i - 1) & 1);
        cout << endl;

        if (!(n & testNum) && n != startNum)
            r_nCr(n, bitVal, testNum);

        if (bitVal && bitVal < testNum)
            r_nCr(startNum, bitVal >> 1, testNum);
    }

    你可以在这里看到它是如何工作的解释。


    在Python中,利用递归和所有事情都是通过引用完成的事实。对于非常大的集合,这将占用大量的内存,但其优点是初始集合可以是一个复杂的对象。它只能找到唯一的组合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import copy

    def find_combinations( length, set, combinations = None, candidate = None ):
        # recursive function to calculate all unique combinations of unique values
        # from [set], given combinations of [length].  The result is populated
        # into the 'combinations' list.
        #
        if combinations == None:
            combinations = []
        if candidate == None:
            candidate = []

        for item in set:
            if item in candidate:
                # this item already appears in the current combination somewhere.
                # skip it
                continue

            attempt = copy.deepcopy(candidate)
            attempt.append(item)
            # sorting the subset is what gives us completely unique combinations,
            # so that [1, 2, 3] and [1, 3, 2] will be treated as equals
            attempt.sort()

            if len(attempt) < length:
                # the current attempt at finding a new combination is still too
                # short, so add another item to the end of the set
                # yay recursion!
                find_combinations( length, set, combinations, attempt )
            else:
                # the current combination attempt is the right length.  If it
                # already appears in the list of found combinations then we'll
                # skip it.
                if attempt in combinations:
                    continue
                else:
                    # otherwise, we append it to the list of found combinations
                    # and move on.
                    combinations.append(attempt)
                    continue
        return len(combinations)

    你这样用。传递"result"是可选的,因此您可以使用它来获取可能的组合数…尽管这会非常低效(最好通过计算来完成)。

    1
    2
    3
    4
    5
    6
    7
    size = 3
    set = [1, 2, 3, 4, 5]
    result = []

    num = find_combinations( size, set, result )
    print"size %d results in %d sets" % (size, num)
    print"result: %s" % (result,)

    您应该从该测试数据中获得以下输出:

    1
    2
    size 3 results in 10 sets
    result: [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

    如果你的套装看起来像这样,它也能正常工作:

    1
    2
    3
    4
    5
    6
    7
    set = [
        [ 'vanilla', 'cupcake' ],
        [ 'chocolate', 'pudding' ],
        [ 'vanilla', 'pudding' ],
        [ 'chocolate', 'cookie' ],
        [ 'mint', 'cookie' ]
    ]

    C简单算法。(自从我尝试使用你们上传的那个,我就发布了,但是出于某种原因我不能编译它——扩展一个类?所以我自己写了一封信,以防别人也面临同样的问题。顺便说一句,我对C语言的了解不比基本编程多,但这一个很好用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public static List<List<int>> GetSubsetsOfSizeK(List<int> lInputSet, int k)
            {
                List<List<int>> lSubsets = new List<List<int>>();
                GetSubsetsOfSizeK_rec(lInputSet, k, 0, new List<int>(), lSubsets);
                return lSubsets;
            }

    public static void GetSubsetsOfSizeK_rec(List<int> lInputSet, int k, int i, List<int> lCurrSet, List<List<int>> lSubsets)
            {
                if (lCurrSet.Count == k)
                {
                    lSubsets.Add(lCurrSet);
                    return;
                }

                if (i >= lInputSet.Count)
                    return;

                List<int> lWith = new List<int>(lCurrSet);
                List<int> lWithout = new List<int>(lCurrSet);
                lWith.Add(lInputSet[i++]);

                GetSubsetsOfSizeK_rec(lInputSet, k, i, lWith, lSubsets);
                GetSubsetsOfSizeK_rec(lInputSet, k, i, lWithout, lSubsets);
            }

    用法:GetSubsetsOfSizeK(set of type List, integer k)

    您可以修改它来迭代您正在处理的任何内容。

    祝你好运!


    这是一个C++解决方案,我使用递归和位移位来解决。它也可以用C语言工作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void r_nCr(unsigned int startNum, unsigned int bitVal, unsigned int testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
    {
        unsigned int n = (startNum - bitVal) << 1;
        n += bitVal ? 1 : 0;

        for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
            cout << (n >> (i - 1) & 1);
        cout << endl;

        if (!(n & testNum) && n != startNum)
            r_nCr(n, bitVal, testNum);

        if (bitVal && bitVal < testNum)
            r_nCr(startNum, bitVal >> 1, testNum);
    }

    你可以在这里找到这是如何工作的解释。


    短时间快速C实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public static IEnumerable<IEnumerable<T>> Combinations<T>(IEnumerable<T> elements, int k)
    {
        return Combinations(elements.Count(), k).Select(p => p.Select(q => elements.ElementAt(q)));                
    }      

    public static List<int[]> Combinations(int setLenght, int subSetLenght) //5, 3
    {
        var result = new List<int[]>();

        var lastIndex = subSetLenght - 1;
        var dif = setLenght - subSetLenght;
        var prevSubSet = new int[subSetLenght];
        var lastSubSet = new int[subSetLenght];
        for (int i = 0; i < subSetLenght; i++)
        {
            prevSubSet[i] = i;
            lastSubSet[i] = i + dif;
        }

        while(true)
        {
            //add subSet ad result set
            var n = new int[subSetLenght];
            for (int i = 0; i < subSetLenght; i++)
                n[i] = prevSubSet[i];

            result.Add(n);

            if (prevSubSet[0] >= lastSubSet[0])
                break;

            //start at index 1 because index 0 is checked and breaking in the current loop
            int j = 1;
            for (; j < subSetLenght; j++)
            {
                if (prevSubSet[j] >= lastSubSet[j])
                {
                    prevSubSet[j - 1]++;

                    for (int p = j; p < subSetLenght; p++)
                        prevSubSet[p] = prevSubSet[p - 1] + 1;

                    break;
                }
            }

            if (j > lastIndex)
                prevSubSet[lastIndex]++;
        }

        return result;
    }

    也许我遗漏了这一点(您需要的是算法,而不是现成的解决方案),但scala似乎是开箱即用的(现在):

    1
    2
    3
    def combis(str:String, k:Int):Array[String] = {
      str.combinations(k).toArray
    }

    使用如下方法:

    1
      println(combis("abcd",2).toList)

    将产生:

    1
      List(ab, ac, ad, bc, bd, cd)

    这是我在javascript中的贡献(无递归)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    set = ["q0","q1","q2","q3"]
    collector = []


    function comb(num) {
      results = []
      one_comb = []
      for (i = set.length - 1; i >= 0; --i) {
        tmp = Math.pow(2, i)
        quotient = parseInt(num / tmp)
        results.push(quotient)
        num = num % tmp
      }
      k = 0
      for (i = 0; i < results.length; ++i)
        if (results[i]) {
          ++k
          one_comb.push(set[i])
        }
      if (collector[k] == undefined)
        collector[k] = []
      collector[k].push(one_comb)
    }


    sum = 0
    for (i = 0; i < set.length; ++i)
      sum += Math.pow(2, i)
     for (ii = sum; ii > 0; --ii)
      comb(ii)
     cnt = 0
    for (i = 1; i < collector.length; ++i) {
      n = 0
      for (j = 0; j < collector[i].length; ++j)
        document.write(++cnt," -" + (++n) +" -", collector[i][j],"")
      document.write("")
    }


    这是一个coffeescript实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    combinations: (list, n) ->
            permuations = Math.pow(2, list.length) - 1
            out = []
            combinations = []

            while permuations
                out = []

                for i in [0..list.length]
                    y = ( 1 << i )
                    if( y & permuations and (y isnt permuations))
                        out.push(list[i])

                if out.length <= n and out.length > 0
                    combinations.push(out)

                permuations--

            return combinations

    还有另一个递归解决方案(您应该能够将其移植到使用字母而不是数字的端口),它使用的堆栈比大多数版本都要短一些:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    stack = []
    def choose(n,x):
       r(0,0,n+1,x)

    def r(p, c, n,x):
       if x-c == 0:
          print stack
          return

       for i in range(p, n-(x-1)+c):
          stack.append(i)
          r(i+1,c+1,n,x)
          stack.pop()

    4选择3,或者我要从0到4开始的所有3个数字组合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    choose(4,3)

    [0, 1, 2]
    [0, 1, 3]
    [0, 1, 4]
    [0, 2, 3]
    [0, 2, 4]
    [0, 3, 4]
    [1, 2, 3]
    [1, 2, 4]
    [1, 3, 4]
    [2, 3, 4]

    下面是一个使用宏的Lisp方法。这在普通的lisp中有效,并且应该在其他lisp方言中有效。

    下面的代码创建"n"嵌套循环,并为列表lst中的每个"n"元素组合执行任意代码块(存储在body变量中)。变量var指向一个包含循环所用变量的列表。

    1
    2
    3
    4
    5
    6
    7
    (defmacro do-combinations ((var lst num) &body body)
      (loop with syms = (loop repeat num collect (gensym))
            for i on syms
            for k = `(loop for ,(car i) on (cdr ,(cadr i))
                             do (let ((,var (list ,@(reverse syms)))) (progn ,@body)))
                    then `(loop for ,(car i) on ,(if (cadr i) `(cdr ,(cadr i)) lst) do ,k)
            finally (return k)))

    让我们看看…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    (macroexpand-1 '(do-combinations (p '(1 2 3 4 5 6 7) 4) (pprint (mapcar #'car p))))

    (LOOP FOR #:G3217 ON '(1 2 3 4 5 6 7) DO
     (LOOP FOR #:G3216 ON (CDR #:G3217) DO
      (LOOP FOR #:G3215 ON (CDR #:G3216) DO
       (LOOP FOR #:G3214 ON (CDR #:G3215) DO
        (LET ((P (LIST #:G3217 #:G3216 #:G3215 #:G3214)))
         (PROGN (PPRINT (MAPCAR #'CAR P))))))))

    (do-combinations (p '(1 2 3 4 5 6 7) 4) (pprint (mapcar #'car p)))

    (1 2 3 4)
    (1 2 3 5)
    (1 2 3 6)
    ...

    由于默认情况下不存储组合,因此存储量保持在最小值。选择body代码而不是存储所有结果的可能性也提供了更大的灵活性。


    这个答案怎么样……这个打印所有长度3的组合……它可以概括为任何长度……工作代码…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include<iostream>
    #include<string>
    using namespace std;

    void combination(string a,string dest){
    int l = dest.length();
    if(a.empty() && l  == 3 ){
     cout<<dest<<endl;}
    else{
      if(!a.empty() && dest.length() < 3 ){
         combination(a.substr(1,a.length()),dest+a[0]);}
      if(!a.empty() && dest.length() <= 3 ){
          combination(a.substr(1,a.length()),dest);}
     }

     }

     int main(){
     string demo("abcd");
     combination(demo,"");
     return 0;
     }

    下面是我的javascript解决方案,通过使用reduce/map,它可以消除几乎所有的变量,使其功能更加强大。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function combinations(arr, size) {
      var len = arr.length;

      if (size > len) return [];
      if (!size) return [[]];
      if (size == len) return [arr];

      return arr.reduce(function (acc, val, i) {
        var res = combinations(arr.slice(i + 1), size - 1)
          .map(function (comb) { return [val].concat(comb); });
       
        return acc.concat(res);
      }, []);
    }

    var combs = combinations([1,2,3,4,5,6,7,8],3);
    combs.map(function (comb) {
      document.body.innerHTML += comb.toString() + '<br />';
    });

    document.body.innerHTML += '<br /> Total combinations = ' + combs.length;


    C/C++中的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    #include <unistd.h>
    #include <stdio.h>
    #include <iconv.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>

    int main(int argc, char **argv)
    {
        int opt = -1, min_len = 0, max_len = 0;
        char ofile[256], fchar[2], tchar[2];
        ofile[0] = 0;
        fchar[0] = 0;
        tchar[0] = 0;
        while((opt = getopt(argc, argv,"o:f:t:l:L:")) != -1)
        {
                switch(opt)
                {
                        case 'o':
                        strncpy(ofile, optarg, 255);
                        break;
                        case 'f':
                        strncpy(fchar, optarg, 1);
                        break;
                        case 't':
                        strncpy(tchar, optarg, 1);
                        break;
                        case 'l':
                        min_len = atoi(optarg);
                        break;
                        case 'L':
                        max_len = atoi(optarg);
                        break;
                        default:
                        printf("usage: %s -oftlL
    \t-o output file
    \t-f from char
    \t-t to char
    \t-l min seq len
    \t-L max seq len", argv[0]);
                }
        }
    if(max_len < 1)
    {
        printf("error, length must be more than 0
    ");
        return 1;
    }
    if(min_len > max_len)
    {
        printf("error, max length must be greater or equal min_length
    ");
        return 1;
    }
    if((int)fchar[0] > (int)tchar[0])
    {
        printf("error, invalid range specified
    ");
        return 1;
    }
    FILE *out = fopen(ofile,"w");
    if(!out)
    {
        printf("failed to open input file with error: %s
    ", strerror(errno));
        return 1;
    }
    int cur_len = min_len;
    while(cur_len <= max_len)
    {
        char buf[cur_len];
        for(int i = 0; i < cur_len; i++)
            buf[i] = fchar[0];
        fwrite(buf, cur_len, 1, out);
        fwrite("
    ", 1, 1, out);
        while(buf[0] != (tchar[0]+1))
        {
            while(buf[cur_len-1] < tchar[0])
            {
                (int)buf[cur_len-1]++;
                fwrite(buf, cur_len, 1, out);
                fwrite("
    ", 1, 1, out);
            }
            if(cur_len < 2)
                break;
            if(buf[0] == tchar[0])
            {
                bool stop = true;
                for(int i = 1; i < cur_len; i++)
                {
                    if(buf[i] != tchar[0])
                    {
                        stop = false;
                        break;
                    }
                }
                if(stop)
                    break;
            }
            int u = cur_len-2;
            for(; u>=0 && buf[u] >= tchar[0]; u--)
                ;
            (int)buf[u]++;
            for(int i = u+1; i < cur_len; i++)
                buf[i] = fchar[0];
            fwrite(buf, cur_len, 1, out);
            fwrite("
    ", 1, 1, out);
        }
        cur_len++;
    }
    fclose(out);
    return 0;
    }

    这里我在C++中实现,它将所有的组合写入指定的文件,但是行为可以改变,我制作了各种字典,它接受最小和最大长度和字符范围,目前只支持ANSI,它满足了我的需要。


    如果你是C++用户,你可以利用NExtx置换函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    string name ="abcdefg";
    int desiredCharsNumber = 3;
    string chosenChars = string(name.size()-desiredCharsNumber,'0') + string(desiredCharsNumber, '1');
    do {
        for (int i = 0; i < name.size(); ++i)
            if (chosenChars[i] == '1')
                cout << name[i];
        cout << endl;
    } while (next_permutation(chosenChars.begin(), chosenChars.end()));