web-dev-qa-db-fra.com

Algorithme pour renvoyer toutes les combinaisons de k éléments de n

Je veux écrire une fonction qui prend un tableau de lettres en argument et un certain nombre de ces lettres à sélectionner. 

Supposons que vous fournissiez un tableau de 8 lettres et que vous souhaitiez en sélectionner 3. Ensuite, vous devriez obtenir:

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

Tableaux (ou mots) en retour composé de 3 lettres chacun.

528
Fredrik

Art of Computer Programming Volume 4: Fascicule 3 en contient une tonne qui conviendrait mieux à votre situation particulière que ce que je décris.

Codes gris

Un problème que vous rencontrerez est bien sûr la mémoire et assez rapidement, vous aurez des problèmes de 20 éléments dans votre ensemble - 20C3 = 1140. Et si vous souhaitez parcourir l'ensemble, il est préférable d'utiliser un algorithme de code gris modifié afin de ne pas les conserver tous en mémoire. Ceux-ci génèrent la combinaison suivante de la précédente et évitent les répétitions. Il y en a beaucoup pour différents usages. Voulons-nous maximiser les différences entre les combinaisons successives? minimiser? etc.

Certains des articles originaux décrivant les codes gris: 

  1. Quelques chemins Hamilton et un algorithme de changement minimal
  2. Algorithme de génération de combinaison d'échange adjacente

Voici quelques autres articles sur le sujet:

  1. Une implémentation efficace de l'algorithme de génération de combinaison d'échange Eades, Hickey, Lecture Adjacent (PDF, avec code en Pascal)
  2. Générateurs combinés
  3. Enquête sur les codes gris combinatoires (PostScript)
  4. Un algorithme pour les codes gris

Le Twiddle de Chase (algorithme)

Phillip J Chase, ` Algorithme 382: Combinaisons de M sur N objets '(1970)

L'algorithme en C ...

Index des combinaisons en ordre lexicographique (algorithme de boucles 515)

Vous pouvez également référencer une combinaison par son index (par ordre lexicographique). En réalisant que l’indice devrait être une sorte de changement de droite à gauche basé sur l’index, nous pouvons construire quelque chose qui devrait récupérer une combinaison.

Nous avons donc un ensemble {1,2,3,4,5,6} ... et nous voulons trois éléments. Disons que {1,2,3} nous pouvons dire que la différence entre les éléments est un et dans l’ordre et minimale. {1,2,4} comporte un changement et est lexicographiquement le numéro 2. Ainsi, le nombre de «changements» à la dernière place compte pour un changement dans l'ordre lexicographique. La deuxième place, avec un changement, {1,3,4}, a un changement, mais représente plus de changements depuis sa deuxième place (proportionnelle au nombre d'éléments dans l'ensemble d'origine).

La méthode que j'ai décrite est une déconstruction, semble-t-il, de jeu en index, nous devons faire l'inverse - ce qui est beaucoup plus délicat. C’est ainsi que Buckles résout le problème. J'ai écrit C pour les calculer , avec des modifications mineures - j'ai utilisé l'indice des ensembles plutôt qu'une plage de nombres pour représenter l'ensemble, nous travaillons donc toujours à partir de 0 ... n .

  1. Puisque les combinaisons ne sont pas ordonnées, {1,3,2} = {1,2,3} - nous les ordonnons d'être lexicographiques.
  2. Cette méthode a un 0 implicite pour démarrer l'ensemble pour la première différence.

Index des combinaisons en ordre lexicographique (McCaffrey)

Il y a une autre manière :, son concept est plus facile à comprendre et à programmer, mais sans les optimisations de Buckles. Heureusement, cela ne produit pas non plus de combinaisons dupliquées:

L'ensemble x_k...x_1 in N qui maximise i = C(x_1,k) + C(x_2,k-1) + ... + C(x_k,1) , où C(n,r) = {n choose r} .

Pour un exemple: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1). La 27ème combinaison lexicographique de quatre choses est donc: {1,2,5,6}, ce sont les index de l’ensemble que vous voulez regarder. Exemple ci-dessous (OCaml), requiert la fonction choose, laissée au lecteur:

(* 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)
388
nlucaroni

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

Usage:

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

Résultat:

123
124
125
134
135
145
234
235
245
345
186
user230950

Puis-je présenter ma solution récursive Python à ce problème? 

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

Exemple d'utilisation:

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

Je l'aime pour sa simplicité.

71
Claudiu

Solution Java courte:

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

Le résultat sera

[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]
69
user935714

Disons que votre tableau de lettres ressemble à ceci: "ABCDEFGH". Vous avez trois index (i, j, k) indiquant les lettres que vous allez utiliser pour le mot actuel. Vous commencez par:

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

Tout d'abord, vous variez k, donc la prochaine étape ressemble à ça:

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

Si vous avez atteint la fin, vous continuez et vous modifiez j puis k à nouveau.

 A B C D E F G H 
 ^ ^ ^ 

Une fois que vous avez atteint G, vous commencez également à varier i.

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

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

Écrit en code ce look quelque chose comme ça

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]);
        }
    }
}
60
quinmars

L'algorithme récursif suivant sélectionne toutes les combinaisons d'éléments k à partir d'un ensemble ordonné:

  • choisissez le premier élément i de votre combinaison
  • combine i avec chacune des combinaisons d'éléments k-1 choisies récursivement dans l'ensemble d'éléments supérieurs à i.

Itérez ce qui précède pour chaque i de l'ensemble.

Il est essentiel que vous choisissiez le reste des éléments avec une taille supérieure à i pour éviter les répétitions. De cette façon, [3,5] sera sélectionné une seule fois, comme [3] combiné avec [5], au lieu de deux (la condition élimine [5] + [3]). Sans cette condition, vous obtenez des variations au lieu de combinaisons.

49
Rafał Dowgird

J'ai trouvé ce fil utile et j'ai pensé ajouter une solution Javascript que vous pouvez utiliser dans Firebug. En fonction de votre moteur JS, cela peut prendre un peu de temps si la chaîne de départ est grande.

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

La sortie devrait être comme suit:

abc
ab
ac
a
bc
b
c
24
Adam

En C++, la routine suivante produira toutes les combinaisons de longueur distance (premier, k) entre l’intervalle [premier, dernier):

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

Il peut être utilisé comme ceci:

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

Ceci imprimera ce qui suit:

123
124
125
134
135
145
234
235
245
345
23
Matthieu N.
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;
    }
}
20
Adam Hughes

Petit exemple en Python:

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

Pour explication, la méthode récursive est décrite à l'aide de l'exemple suivant: 

Exemple: A B C D E
Toutes les combinaisons de 3 seraient:

  • A avec toutes les combinaisons de 2 du reste (B C D E)
  • B avec toutes les combinaisons de 2 du reste (C D E)
  • C avec toutes les combinaisons de 2 du reste (D E)
17
Rick Giuly

Algorithme récursif simple en Haskell

import Data.List

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

Nous définissons d’abord le cas particulier, c’est-à-dire en sélectionnant zéro élément. Il produit un seul résultat, qui est une liste vide (c’est-à-dire une liste contenant une liste vide).

Pour n> 0, x parcourt tous les éléments de la liste et xs correspond à tous les éléments après x.

rest sélectionne n - 1 éléments de xs à l'aide d'un appel récursif à combinations. Le résultat final de la fonction est une liste où chaque élément est x : rest (c’est-à-dire une liste avec x en tête et rest en queue) pour chaque valeur différente de x et rest.

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

Et bien sûr, Haskell étant paresseux, la liste est générée au fur et à mesure, de sorte que vous pouvez évaluer partiellement des combinaisons exponentiellement grandes.

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

Et voici granddaddy COBOL, le langage tant décrié.

Supposons un tableau de 34 éléments de 8 octets chacun (sélection purement arbitraire). L'idée est d'énumérer toutes les combinaisons possibles de 4 éléments et de les charger dans un tableau.

Nous utilisons 4 indices, un pour chaque position dans le groupe de 4

Le tableau est traité comme ceci:

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

Nous varions idx4 de 4 à la fin. Pour chaque idx4, nous obtenons une combinaison unique De groupes de quatre. Lorsque idx4 arrive à la fin du tableau, nous incrémentons idx3 de 1 et définissons idx4 sur idx3 + 1. Ensuite, nous courons encore idx4 à la fin. Nous procédons de cette manière, en augmentant idx3, idx2 et idx1 respectivement jusqu'à ce que la position de idx1 soit inférieure à 4 à partir de la fin du tableau. Cela termine l'algorithme.

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

Premières itérations:

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

Un exemple 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.
13
Harry Fisher

Voici une implémentation élégante et générique dans Scala, telle que décrite dans 99 Problèmes 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 :: _}
    }
}
9
Zack Marrapese

Si vous pouvez utiliser la syntaxe SQL - par exemple, si vous utilisez LINQ pour accéder aux champs d'une structure ou d'un tableau, ou si vous accédez directement à une base de données contenant une table appelée "Alphabet" avec un seul champ "Lettre", vous pouvez adapter ce qui suit: code:

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

Cela retournera toutes les combinaisons de 3 lettres, quel que soit le nombre de lettres que vous avez dans le tableau "Alphabet" (cela peut être 3, 8, 10, 27, etc.).

Si ce que vous voulez, ce sont toutes les permutations, plutôt que les combinaisons (c'est-à-dire que vous voulez que "ACB" et "ABC" soient considérés différents, plutôt que d'apparaître une fois), supprimez simplement la dernière ligne (AND) et le tour est joué.

Post-édition: Après avoir relu la question, je me rends compte que l’algorithme général est nécessaire, et pas seulement un algorithme spécifique pour le cas de la sélection de 3 éléments. La réponse d'Adam Hughes est complète. Malheureusement, je ne peux pas voter (pour le moment). Cette réponse est simple mais ne fonctionne que lorsque vous voulez exactement 3 éléments.

8
Joe Pineda

J'avais un algorithme de permutation utilisé pour le projet euler, en 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

Si 

n<len(l) 

vous devriez avoir toutes les combinaisons dont vous avez besoin sans répétition, en avez-vous besoin?

C'est un générateur, vous l'utilisez donc dans quelque chose comme ça:

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

Vous avez ici une version d'évaluation paresseuse de cet algorithme codé en 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));
        }
    }

Et partie test:

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

J'espère que cela vous aidera!

6
Juan Antonio Cano

Une autre version C # avec génération paresseuse des indices de combinaison. Cette version gère un tableau unique d’index afin de définir un mappage entre la liste de toutes les valeurs et les valeurs de la combinaison en cours, c’est-à-dire qu’il utilise constamment O(k) espace supplémentaire pendant toute la durée de fonctionnement. Le code génère des combinaisons individuelles, y compris la première, dans O(k) time.

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

Code de test:

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

Sortie:

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
6
Wormbo
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;
}
5
oddi

Version 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))))
5
llj098

https://Gist.github.com/3118596

Il existe une implémentation pour JavaScript. Il a des fonctions pour obtenir k-combinaisons et toutes les combinaisons d'un tableau d'objets quelconques. Exemples:

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]]
5
Akseli Palén

Disons que votre tableau de lettres ressemble à ceci: "ABCDEFGH". Vous avez trois index (i, j, k) indiquant les lettres que vous allez utiliser pour le mot actuel. Vous commencez par:

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

Tout d'abord, vous variez k, donc la prochaine étape ressemble à ça:

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

Si vous avez atteint la fin, vous continuez et vous modifiez j puis k à nouveau.

 A B C D E F G H 
 ^ ^ ^ 

Une fois que vous avez atteint G, vous commencez également à varier i.

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

Basé sur https://stackoverflow.com/a/127898/2628125 , mais plus abstrait, pour toutes les tailles de pointeurs.

4
Oleksandr Knyga

Voici une méthode qui vous donne toutes les combinaisons de taille spécifiée à partir d'une chaîne de longueur aléatoire. Semblable à la solution de quinmars, mais fonctionne pour des entrées variées et k.

Le code peut être changé en boucle, c’est-à-dire 'tamp' à partir de l’entrée 'abcd' 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);
    }
}

Sortie pour "abcde": 

abc abd abe acd ace ade bcd bce bde cde

3
Adrian

Une solution Javascript concise:

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

Voici un code que j'ai récemment écrit en Java, qui calcule et renvoie toute la combinaison d'éléments "num" à partir d'éléments "outOf".

// 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);
    }
}
3
user292949

J'ai créé une solution dans SQL Server 2005 à cet effet et l'ai publiée sur mon site Web: http://www.jessemclain.com/downloads/code/sql/fn_GetMChooseNCombos.sql.htm

Voici un exemple pour montrer l'utilisation:

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

résultats:

Word
----
AB
AC
AD
BC
BD
CD

(6 row(s) affected)
3
Jesse

Tout est dit et fait, voici le code O'caml pour cela . L'algorithme est évident à partir du code ..

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 []
;;
3
rusty

Voici ma proposition en C++

J'ai essayé d'imposer le moins de restrictions possible sur le type d'itérateur, de sorte que cette solution suppose uniquement un itérateur, et qu'il peut s'agir d'un const_iterator. Cela devrait fonctionner avec n'importe quel conteneur standard. Dans les cas où les arguments n'ont pas de sens, std :: invalid_argumnent est lancé

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

Algorithme:

  • Comptez de 1 à 2 ^ n.
  • Convertir chaque chiffre en sa représentation binaire.
  • Traduisez chaque bit 'on' en éléments de votre ensemble, en fonction de la position.

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

Pourquoi ça marche?

Il y a une bijection entre les sous-ensembles d'un ensemble de n éléments et les séquences de n bits.

Cela signifie que nous pouvons déterminer le nombre de sous-ensembles en comptant les séquences. 

par exemple, les quatre éléments ci-dessous peuvent être représentés par {0,1} X {0, 1} X {0, 1} X {0, 1} (ou 2 ^ 4) séquences différentes.

So - tout ce que nous avons à faire, c'est compter de 1 à 2 ^ n pour trouver toutes les combinaisons. (Nous ignorons l'ensemble vide.) Ensuite, traduisez les chiffres en leur représentation binaire. Remplacez ensuite les éléments de votre ensemble par des éléments "actifs".

Si vous ne voulez que k résultats d’éléments, n’imprimez que lorsque k bits sont activés. 

(Si vous voulez tous les sous-ensembles au lieu de k sous-ensembles de longueur, supprimez la partie cnt/kElement.) 

(Pour la preuve, voir MIT le didacticiel gratuit Mathématiques pour l'informatique, Lehman et al, section 11.2.2. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/ 6-042j-mathématiques-pour-informatique-science-automne-2010/lectures/ )

2
jacoblambert

J'ai écrit une classe pour gérer des fonctions communes permettant de travailler avec le coefficient binomial, qui correspond au type de problème auquel votre problème appartient. Il effectue les tâches suivantes:

  1. Affiche tous les K-index au format Nice pour tout N, choisissez K dans un fichier. Les K-index peuvent être remplacés par des chaînes ou des lettres plus descriptives. Cette méthode rend la résolution de ce type de problème assez triviale.

  2. Convertit les K-index en index approprié d'une entrée dans la table des coefficients binomiaux triée. Cette technique est beaucoup plus rapide que les anciennes techniques publiées qui reposent sur l'itération. Pour ce faire, il utilise une propriété mathématique inhérente au triangle de Pascal. Mon article en parle. Je crois que je suis le premier à découvrir et à publier cette technique, mais je peux me tromper.

  3. Convertit l'index d'une table de coefficients binomiale triée en index K correspondants.

  4. Usages Mark Dominus méthode de calcul du coefficient binomial, beaucoup moins susceptible de déborder et fonctionnant avec des nombres plus grands.

  5. La classe est écrite en .NET C # et permet de gérer les objets liés au problème (le cas échéant) à l'aide d'une liste générique. Le constructeur de cette classe prend une valeur bool appelée InitTable qui, lorsqu'elle est vraie, créera une liste générique destinée à contenir les objets à gérer. Si cette valeur est false, la table ne sera pas créée. La table n'a pas besoin d'être créée pour exécuter les 4 méthodes ci-dessus. Des méthodes d'accesseur sont fournies pour accéder à la table.

  6. Il existe une classe de test associée qui montre comment utiliser la classe et ses méthodes. Il a été testé de manière approfondie avec 2 cas et il n'y a pas de bugs connus.

Pour en savoir plus sur cette classe et télécharger le code, voir Tablizing The Binomial Coeffieicent .

Il ne devrait pas être difficile de convertir cette classe en C++.

2
Bob Bryan

Je recherchais une solution similaire pour PHP et suis tombé sur ce qui suit

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, ' ';

la source

Je ne suis pas sûr de l'efficacité de la classe, mais je ne l'utilisais que pour un semoir.

1
Sevenearths

Une macro LISP génère le code pour toutes les valeurs r (prises à la fois)

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

Alors,

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> 

Et,

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> 
1
kes

Algorithme php court pour renvoyer toutes les combinaisons de k éléments de n (coefficient binomial) basées sur la solution Java:

$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 "<pre>";
print_r($array_general);
echo "</pre>";

La même solution mais en javascript:

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

Voici ma solution Scala :

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
Vladimir Kostyukov

Ceci est un programme récursif qui génère des combinaisons pour nCk.Elements dans la collection sont supposées être de 1 à n

#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("\n");
        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\n");
    scanf("%d %d",&n,&k);
    a=(int*)malloc(k*sizeof(int));
    count=nCk(n,k,0,a,k);
    printf("No of combinations=%d\n",count);
}
1
Manohar Bhat

Approche JavaScript, basée sur un générateur, récursive:

function *nCk(n,k){
  for(var i=n-1;i>=k-1;--i)
    if(k===1)
      yield [i];
    else
      for(var temp of nCk(i,k-1)){
        temp.unshift(i);
        yield temp;
      }
}

function test(){
  try{
    var n=parseInt(ninp.value);
    var k=parseInt(kinp.value);
    log.innerText="";
    var stop=Date.now()+1000;
    if(k>=1)
      for(var res of nCk(n,k))
        if(Date.now()<stop)
          log.innerText+=JSON.stringify(res)+" ";
        else{
          log.innerText+="1 second passed, stopping here.";
          break;
        }
  }catch(ex){}
}
n:<input id="ninp" oninput="test()">
&gt;= k:<input id="kinp" oninput="test()"> &gt;= 1
<div id="log"></div>

De cette façon (en diminuant i et unshift()), il produit des combinaisons et des éléments à l’intérieur des combinaisons dans l’ordre décroissant, ce qui plaît un peu à l’œil.
Le test s’arrête après 1 seconde. La saisie de nombres étranges est donc relativement sûre.

1
tevemadar

En VB.Net, cet algorithme collecte toutes les combinaisons de n nombres à partir d’un ensemble de nombres (PoolArray). par exemple. toutes les combinaisons de 5 choix parmi "8,10,20,33,41,44,47".

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

Et voici une version Clojure qui utilise le même algorithme que celui décrit dans ma réponse d'implémentation OCaml:

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

Il existe trois façons de l'appeler:

(a) pour exactement n éléments sélectionnés comme l'exige la question:

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

(b) pour les éléments sélectionnés entre n1 et n2:

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) pour entre 0 et la taille de la collection d'éléments sélectionnés:

user=> (select '(1 2 3))
(() (3) (2) (1) (2 3) (1 3) (1 2) (1 2 3))
1
Marcus Junius Brutus

Code C pour l’algorithme L (Combinaisons lexicographiques) dans la section 7.2.1.3 de L’art de la programmation informatique, volume 4A: algorithmes combinatoires, partie 1 :

#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("\n");
}

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;
}
1
Jingguo Yao
#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('\n');
    } while (next_combination(numbers, n, k));
    getchar();
    return 0;
}
1
monster

Une autre solution avec C #:

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

Exemple d'utilisation:

   List<char> initialArray = new List<char>() { 'a','b','c','d'};
   int combinationLength = 3;
   List<List<char>> combinations = GetCombinations(initialArray, combinationLength);
1
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;
}

Dans la première fonction, m indique combien il vous reste à choisir, et start indique quelle position du tableau vous devez commencer à choisir.

1
Mehmud Abliz

Voici un code simple qui affiche toutes les combinaisons C (n, m). Cela fonctionne en initialisant et en déplaçant un ensemble d'indices de tableau qui pointent vers la prochaine combinaison valide. Les indices sont initialisés pour pointer sur les m indices les plus bas (lexicographiquement, la plus petite combinaison). Ensuite, en commençant par le m-ème index, nous essayons de faire avancer les index. si un index a atteint sa limite, nous essayons le précédent (jusqu'au dernier index). Si nous pouvons avancer d'un index, nous réinitialisons tous les grands indices.

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", 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("\n");

    // 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;
}
1
Nagendra Gulur

Puisque le langage de programmation n’est pas mentionné, je suppose que les listes sont également acceptables. Voici donc une version d’OCaml adaptée aux listes courtes (non-récursive). Étant donné une listeldes éléments de n'importe quel type et un entiernil retournera une liste de toutes les listes possibles contenantnelements oflsi nous supposons que l'ordre des éléments dans les listes de résultats est ignoré, c'est-à-dire que la liste ['a'; 'b'] est la même que ['b'; ';' a '] et sera signalé une fois. La taille de la liste résultante sera donc ((List.length l) Choisissez n).

L’intuition de la récursivité est la suivante: vous prenez la tête de la liste puis effectuez deux appels récursifs:

  • appel récursif 1 (RC1): à la fin de la liste, mais choisissez n-1 éléments
  • appel récursif 2 (RC2): à la fin de la liste, mais choisissez n éléments

pour combiner les résultats récursifs, listez-multipliez (veuillez porter le nom impair) la tête de la liste avec les résultats de RC1, puis ajoutez (@) les résultats de RC2. List-multiply est l'opération suivante lmul:

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

lmul est implémenté dans le code ci-dessous en tant que

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

La récursivité est terminée lorsque la taille de la liste est égale au nombre d'éléments que vous souhaitez choisir. Dans ce cas, vous retournez simplement la liste.

Alors, voici un quatre lignes dans OCaml qui implémente l'algorithme ci-dessus:

    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)   
    | [], _ -> []    
1

Sauter dans le train en marche et publier une autre solution. Ceci est une implémentation Java générique. Entrée: (int k) est le nombre d'éléments à choisir et (List<T> list) est la liste de choix Retourne une liste de combinaisons (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;
}
1
Miraan Tabrez

code python court, donnant les positions d'index

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
1
Nathan Schmidt

Voici une solution JS simple:

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

0
Max Leizerovich

Voici une approche LISP utilisant une macro. Cela fonctionne dans LISP commun et devrait fonctionner dans d'autres dialectes LISP.

Le code ci-dessous crée des boucles imbriquées et exécute un bloc de code arbitraire (stocké dans la variable body) pour chaque combinaison d'éléments 'n' de la liste lst. La variable var pointe sur une liste contenant les variables utilisées pour les boucles.

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

Voyons voir...

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

Puisque les combinaisons ne sont pas stockées par défaut, le stockage est réduit au minimum. La possibilité de choisir le code body au lieu de stocker tous les résultats offre également plus de flexibilité.

0
Paulo Mendes

Voici une solution C++ que j’ai imaginée en utilisant la récursion et le transfert de bits. Cela peut fonctionner en C aussi.

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

Vous pouvez trouver une explication de la façon dont cela fonctionne ici .

0
android927

Voici un algorithme que j'ai développé pour résoudre ce problème. Il est écrit en c ++, mais peut être adapté à pratiquement tous les langages prenant en charge les opérations au niveau des bits.

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

Vous pouvez voir une explication de la façon dont cela fonctionne ici .

0
android927

C # simple algorithm . (Je le publie depuis que j'ai essayé d'utiliser celui que vous avez téléchargé, mais pour une raison quelconque, je ne pouvais pas le compiler - étendre un cours? Alors j'ai écrit le mien juste au cas où quelqu'un d'autre est confronté au même problème que moi) . Je ne suis pas très intéressé par c # plus que par la programmation de base en passant, mais celui-ci fonctionne bien.

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

USAGE: GetSubsetsOfSizeK(set of type List<int>, integer k)

Vous pouvez le modifier pour itérer sur tout ce avec quoi vous travaillez.

Bonne chance!

0
Mockingbird

En Python, tirer parti de la récursivité et du fait que tout est fait par référence. Cela nécessite beaucoup de mémoire pour les très grands ensembles, mais présente l'avantage que l'ensemble initial peut être un objet complexe. Il ne trouvera que des combinaisons uniques.

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)

Vous l'utilisez de cette façon. Passer le résultat est optionnel, vous pouvez donc l'utiliser pour obtenir le nombre de combinaisons possibles ... bien que ce soit vraiment inefficace (il vaut mieux le calculer).

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

Vous devriez obtenir le résultat suivant à partir de ces données de test:

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

Et cela fonctionnera aussi bien si votre set ressemble à ceci:

set = [
    [ 'Vanilla', 'cupcake' ],
    [ 'chocolate', 'pudding' ],
    [ 'Vanilla', 'pudding' ],
    [ 'chocolate', 'cookie' ],
    [ 'mint', 'cookie' ]
]
0
mpounsett

Combinaisons très rapides pour MetaTrader MQL4 implémentées en tant qu'objet itérateur.

Le code est si simple à comprendre.

J'ai testé beaucoup d'algorithmes, celui-ci est vraiment très rapide - environ 3 fois plus vite que la plupart des fonctions next_combination ().

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

Un programme de pilote pour tester la classe d'itérateurs ci-dessus:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
// 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());

}

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

0
Amr Ali

Brève implémentation rapide en C #

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;
}
0
Roberto B

Encore une autre solution récursive (vous devriez pouvoir porter ceci pour utiliser des lettres au lieu de chiffres) en utilisant une pile, un peu plus courte que la plupart des: 

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 choisissez 3 ou je veux les 3 combinaisons de chiffres commençant par 0 à 4

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]
0
Jolly1234

J'aimerais présenter ma solution. Pas d'appels récursifs, ni boucles imbriquées dans next. Le noyau du code est la méthode next().

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

Et exemple d'utilisation:

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

Voici une implémentation de Coffeescript 

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 
0
Loourr

J'ai peut-être mal compris (que vous avez besoin de l'algorithme et non de la solution prête à l'emploi), mais il semble que scala le fasse tout de suite (maintenant):

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

En utilisant la méthode comme ceci:

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

Produira:

  List(ab, ac, ad, bc, bd, cd)
0
Vladimir M

Ceci est ma contribution en javascript (pas de récursivité)

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], "<br>")
  document.write("<hr>")
}   
0
Tsiros.P

Récursivement, une réponse très simple, combo, en Free Pascal.

    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;

"producteur" dispose du produit défini pour chaque combinaison.

0
Lor

Il n'y a pas besoin de manipulations de collection. Le problème est presque identique au fait de parcourir K boucles imbriquées, mais vous devez faire attention aux index et aux limites (en ignorant les éléments Java et OOP):

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

Utilisez comme si:

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

Obtenir les résultats attendus:

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

Enfin, mappez les index sur l’ensemble de données dont vous disposez.

0
julius

Mise en œuvre rapide rapide C

#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("\n"); }
       else            { comb[++i] = comb[i - 1]; }
    } else i--; }
}

Pour voir à quelle vitesse il est, utilisez ce code et testez-le

#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("\n"); } */
       if (i == p - 1) c++;
       else            { comb[++i] = comb[i - 1]; }
    } else i--; }
  printf("%d!%d == %d combination(s) in %15.3f second(s)\n ", p, n, c, clock()/1000.0);
}

test avec cmd.exe (Windows):

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>

Bonne journée.

0
ManAndPC

Voici ma solution JavaScript qui est un peu plus fonctionnelle grâce à l'utilisation de réduction/carte, qui élimine presque toutes les variables

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;

0
Akkuma

Que diriez-vous de cette réponse ... ceci affiche toutes les combinaisons de longueur 3 ... et il peut généralisé pour n'importe quelle longueur ... Code de travail ...

#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;
 }
0
Sree Ram

Nous pouvons utiliser le concept de bits pour le faire. Laissons une chaîne de caractères "abc" et souhaitons avoir toutes les combinaisons d'éléments de longueur 2 (c'est-à-dire "ab", "ac", "bc"). 

Nous pouvons trouver les bits définis dans des nombres allant de 1 à 2 ^ n (exclusif). Ici, de 1 à 7, et où nous avons défini bits = 2, nous pouvons imprimer la valeur correspondante à partir de chaîne.

par exemple:

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


Exemple de code:

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);
    }
}
0
Sarthak Gupta

J'ai créé une classe générale pour les combinaisons en C++ . Elle est utilisée comme ceci.

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

Ma bibliothèque ncr [i] retourne à partir de 1, pas de 0. C'est pourquoi il y a 0 dans le tableau. Si vous voulez tenir compte de l'ordre, il suffit de modifier la classe nCr en nPr . L'utilisation est identique.

Résultat

ABC ABD ABE ABF ABG ABH ACD ACE ACF ACG ACH ADE __. ADG ADH AEF AEG AEH AFG AFH AGH .__ BCD BCE BCF BCG .__ BCH BDE BDF BDG BDH BEF BE.. BEH BFG BFH BGH __. CDG CDH CEF CEG CEH CFG CFH CGH DEF DE. DFH DGH EFG EFH EGH FGH

Voici le fichier d'en-tête.

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

Et la mise en œuvre.

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

Algorithme de retour arrière C++ simple mais lent.

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

En Python comme Andrea Ambu, mais pas codé en dur pour choisir trois.

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   
0
esiegel

En suivant le code Haskell, calculez le nombre combinaison en même temps , et grâce à la paresse de Haskell, vous pouvez obtenir une partie sans calculer l'autre.

import Data.Semigroup
import Data.Monoid

data Comb = MkComb {count :: Int, combinations :: [[Int]]} deriving (Show, Eq, Ord)

instance Semigroup Comb where
    (MkComb c1 cs1) <> (MkComb c2 cs2) = MkComb (c1 + c2) (cs1 ++ cs2)

instance Monoid Comb where
    mempty = MkComb 0 []

addElem :: Comb -> Int -> Comb
addElem (MkComb c cs) x = MkComb c (map (x :) cs)

comb :: Int -> Int -> Comb
comb n k | n < 0 || k < 0 = error "error in `comb n k`, n and k should be natural number"
comb n k | k == 0 || k == n = MkComb 1 [(take k [k-1,k-2..0])]
comb n k | n < k = mempty
comb n k = comb (n-1) k <> (comb (n-1) (k-1) `addElem` (n-1))

Cela fonctionne comme:

*Main> comb 0 1
MkComb {count = 0, combinations = []}

*Main> comb 0 0
MkComb {count = 1, combinations = [[]]}

*Main> comb 1 1
MkComb {count = 1, combinations = [[0]]}

*Main> comb 4 2
MkComb {count = 6, combinations = [[1,0],[2,0],[2,1],[3,0],[3,1],[3,2]]}

*Main> count (comb 10 5)
252
0
luochen1990