我想写一个功能,需要一系列信件作为参数,一定数量的那些字母的选择。

说你提供一系列8中的信函并要选择3个字母。那么你应该得到:

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

阵列(或字)在返回组成的3个字母的每一个。

有帮助吗?

解决方案

某些汉密尔顿路径和最小变更算法

  • Eades,Hickey,Read related Interchange的高效实施组合生成算法(PDF,代码为Pascal)
  • 组合生成器
  • 组合格雷码调查(PostScript)
  • 格雷码算法
  • Chase's Twiddle(算法)

    Phillip J Chase,`算法382:N个对象中M的组合'(1970)

    C中的算法 ......

    字典顺序中的组合索引(带扣算法515)

    您还可以通过其索引(按字典顺序)引用组合。根据索引意识到索引应该是从右到左的一些变化,我们可以构造一些应该恢复组合的东西。

    所以,我们有一套{1,2,3,4,5,6} ......我们想要三个元素。让我们说{1,2,3}我们可以说元素之间的差异是一个,有序和最小。 {1,2,4}有一个变化,按字典顺序排列第2位。因此,最后一个地方的“变化”数量占字典顺序的一个变化。第二个位置,只有一个变化{1,3,4}有一个变化,但由于它位于第二位(与原始集合中的元素数量成比例),因此会有更多变化。

    我所描述的方法是解构,似乎从设置到索引,我们需要做反向<!>#8211;这更棘手。这就是 Buckles 解决问题的方法。我写了一些 C来计算它们,稍作修改< >!#8211;我使用集合的索引而不是数字范围来表示集合,因此我们总是从0 ... n开始工作。 注意:

    1. 由于组合是无序的,{1,3,2} = {1,2,3} - 我们将它们命名为词典。
    2. 此方法有一个隐式0来启动第一个差异的设置。
    3. 词典中的组合索引hical Order(McCaffrey)

      另一种方式:它的概念更容易掌握和编程,但它没有Buckles的优化。幸运的是,它也不会产生重复的组合:

      设置 最大化 ,其中

      举个例子:27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)。因此,第四十七个词典组合的四个事物是:{1,2,5,6},这些是你想要看的任何集合的索引。下面的示例(OCaml),需要choose功能,留给读者:

      (* 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( n C k )。内存消耗受k约束。

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

      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循环,而是使用递归,

      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#中:

    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)));
    }
    

    用法:

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

    结果:

    123
    124
    125
    134
    135
    145
    234
    235
    245
    345
    

    简短的java解决方案:

    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);
            }
        }       
    }
    

    结果将是

    [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解决方案来解决这个问题吗?

    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))
    

    使用示例:

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

    我喜欢它的简单。

    让我们说你的字母数组如下:<!> quot; ABCDEFGH <!> quot;。你有三个索引(i,j,k)表示你将用于当前单词的字母,你可以从:

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

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

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

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

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

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

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

    用代码编写,看起来像那样

    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\n", string[i], string[j], string[k]);
            }
        }
    }
    

    以下递归算法挑选所有的k-元件组合从一个有序的设置:

    • 选择的第一个元素 i 你结合
    • 相结合 i 每个组合 k-1 元素选择递归集的元素比较大 i.

    迭代上述每 i 在设定。

    至关重要的是,你选择的其他因素作比较大 i, ,避免重复。这种方式[3,5]将只一次,作为[3]结合[5],而不是两次(条件消除了[5]+[3]).没有这个条件你得到变化而不是组合。

    我发现这个线程很有用,并且我认为我会添加一个Javascript解决方案,你可以将其弹出到Firebug中。根据您的JS引擎,如果起始字符串很大,可能需要一些时间。

    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");
    

    输出应如下:

    abc
    ab
    ac
    a
    bc
    b
    c
    

    在C ++中,以下例程将生成[first,last]范围之间的长度距离(first,k)的所有组合:

    #include <algorithm>
    
    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;
    }
    

    可以像这样使用:

    #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;
    }
    

    这将打印以下内容:

    123
    124
    125
    134
    135
    145
    234
    235
    245
    345
    
    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;
        }
    }
    

    简短的例子,在蟒蛇:

    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将是:

    • 一个与所有组合中的2个从其余部分(B C D E)
    • B所有的组合,2从其余部分(C D E)
    • C与所有组合中的2个从其余部分(D E)

    Haskell中的简单递归算法

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

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

    对于n <!> gt; 0,x遍历列表的每个元素,xsrest之后的每个元素。

    n - 1使用递归调用combinationsx : rest中选择<=>元素。函数的最终结果是一个列表,其中每个元素都是<=>(即<=>作为头部,<=>作为尾部的列表),用于<=>和<=>的每个不同值。

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

    当然,由于Haskell很懒,所以列表会根据需要逐步生成,因此您可以部分评估指数级大组合。

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

    这就是爷爷COBOL,这是一种备受诽谤的语言。

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

    我们使用4个指数,每个位置对应4个组中的每个位置

    数组的处理方式如下:

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

    我们将idx4从4改为结尾。对于每个idx4,我们得到一个独特的组合 四人小组。当idx4到达数组的末尾时,我们将idx3递增1并将idx4设置为idx3 + 1。然后我们再次运行idx4到最后。我们以这种方式进行,分别扩充idx3,idx2和idx1,直到idx1的位置从数组的末尾开始小于4。这完成了算法。

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

    第一次迭代:

    1234
    1235
    1236
    1237
    1245
    1246
    1247
    1256
    1257
    1267
    etc.
    

    COBOL示例:

    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.
    

    * 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.
    

    这是Scala中优雅的通用实现,如 99 Scala问题中所述。

    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 :: _}
        }
    }
    

    如果您可以使用SQL语法 - 例如,如果您使用LINQ访问结构或数组的字段,或者直接访问具有名为<!>的表的数据库,则使用; <!>只有一个字段<!>“;字母<!>”,您可以调整以下代码:

    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个字母的所有组合,尽管表格中有多少个字母<!>“Alphabet <!>”; (它可以是3,8,10,27等)。

    如果您想要的是所有排列,而不是组合(即您希望<!>“ACB <!>”和<!>“ABC <!>”)计算为不同,而不是仅仅出现一次)只删除最后一行(AND一次)并完成。

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

    另一个具有懒惰生成组合索引的C#版本。此版本维护单个索引数组,以定义所有值列表与当前组合值之间的映射,即在整个运行时期间不断使用 O(k)附加空间。该代码在 O(k)时间内生成单独的组合,包括第一个组合。

    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;
            }
        }
    }
    

    测试代码:

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

    输出:

    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
    

    这里有一个用C#编码的算法的惰性评估版本:

        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));
            }
        }
    

    测试部分:

        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));
            }
        }
    

    希望这对你有帮助!

    我有一个用于项目euler的排列算法,在python中:

    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
    

    如果

    n<len(l) 
    

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

    它是一个生成器,所以你可以使用它:

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

    https://gist.github.com/3118596

    JavaScript有一个实现。它具有获取k组合和任何对象数组的所有组合的功能。例子:

    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]]
    
    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;
    }
    

    Clojure版本:

    (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))))
    

    让我们说你的字母数组如下所示:<!> quot; ABCDEFGH <!> quot;。你有三个索引(i,j,k)表示你将用于当前单词的字母,你可以从:

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

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

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

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

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

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

    A B C D E F G H
      ^ ^ ^
      i j k
    
    A B C D E F G H
      ^ ^   ^
      i j   k
    ...
    
    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 ,但更抽象,适用于任何尺寸指针。

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

    以下是显示用法的示例:

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

    结果:

    Word
    ----
    AB
    AC
    AD
    BC
    BD
    CD
    
    (6 row(s) affected)
    

    这是我在C ++中的命题

    我试图对迭代器类型施加尽可能少的限制,因此这个解决方案假设只是前向迭代器,它可以是一个const_iterator。这适用于任何标准容器。如果参数没有意义,它会抛出std :: invalid_argumnent

    #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));
        }
    }
    

    所有人都说过并完成了O'caml代码。 从代码中可以看出算法很明显。

    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中编写的代码,它计算并返回<!> quot; num <!> quot;的所有组合。来自<!>“outOf <!>的元素元件。

    // author: Sourabh Bhat (heySourabh@gmail.com)
    
    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);
        }
    }
    

    一个简洁的Javascript解决方案:

    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);
    

    这是一个方法,它为您提供随机长度字符串中指定大小的所有组合。与quinmars的解决方案类似,但适用于各种输入和k。

    代码可以更改为环绕,即输入'abcd'的'dab'w k = 3.

    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

    我编写了一个类来处理使用二项式系数的常用函数,这是您的问题所处的问题类型。它执行以下任务:

    1. 以任意N选择K到文件的格式输出所有K索引。 K索引可以用更具描述性的字符串或字母代替。这种方法使解决这类问题变得非常简单。

    2. 将K索引转换为已排序二项系数表中条目的正确索引。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现。我的论文谈到了这一点。我相信我是第一个发现和发布这种技术的人,但我可能错了。

    3. 将已排序的二项系数表中的索引转换为相应的K索引。

    4. 使用 Mark Dominus 方法计算二项式系数,这是多少不太可能溢出并使用更大的数字。

    5. 该类是用.NET C#编写的,它提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用名为InitTable的bool值,当为true时,将创建一个通用列表来保存要管理的对象。如果此值为false,则不会创建表。不需要创建表来执行上述4种方法。提供访问者方法来访问该表。

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

    7. 要阅读本课程并下载代码,请参阅 Tablizing The Binomial Coeffieicent

      将此类转换为C ++应该不难。

    算法:

    • 从1数到2^正。
    • 把每个位于其二元表示。
    • 翻译每一个"开"的位素的设定,基于的位置。

    在C#:

    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找到所有的组合。 (我们忽略空集。) 接下来,把该数字到他们的二进制代表性。然后替换元件的设置对于"开"的位。

    如果你只想要k元结果,只有印当k位'on'。

    (如果你想要所有的子集,而不是k长度子集,除cnt/kElement的一部分。)

    (证明,请参阅麻省理工学院免费课件的数学计算机科学、雷曼兄弟et al.,部分11.2.2. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-fall-2010/readings/ )

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

    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;
    }
    
    许可以下: CC-BY-SA归因
    不隶属于 StackOverflow
    scroll top