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.
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.
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:
Voici quelques autres articles sur le sujet:
Phillip J Chase, ` Algorithme 382: Combinaisons de M sur N objets '(1970)
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 .
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 qui maximise , où .
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)
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
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é.
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]
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]);
}
}
}
L'algorithme récursif suivant sélectionne toutes les combinaisons d'éléments k à partir d'un ensemble ordonné:
i
de votre combinaisoni
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.
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
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
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;
}
}
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:
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"]
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.
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 :: _}
}
}
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.
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
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!
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
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;
}
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))))
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]]
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.
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
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);
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);
}
}
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)
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 []
;;
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));
}
}
Algorithme:
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/ )
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:
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.
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.
Convertit l'index d'une table de coefficients binomiale triée en index K correspondants.
Usages Mark Dominus méthode de calcul du coefficient binomial, beaucoup moins susceptible de déborder et fonctionnant avec des nombres plus grands.
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.
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++.
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, ' ';
Je ne suis pas sûr de l'efficacité de la classe, mais je ne l'utilisais que pour un semoir.
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>
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);
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)
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);
}
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()">
>= k:<input id="kinp" oninput="test()"> >= 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.
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)
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))
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;
}
#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;
}
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);
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.
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;
}
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:
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)
| [], _ -> []
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;
}
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
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
});
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é.
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 .
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 .
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!
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' ]
]
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
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;
}
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]
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);
}
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
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)
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>")
}
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.
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.
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.
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;
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;
}
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:
print ab (str[0] , str[1])
print ac (str[0] , str[2])
print ab (str[1] , str[2])
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);
}
}
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;
}
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;
}
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
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