Art of Computer Programming Volume 4: Fascicle 3 en a une tonne qui pourrait mieux s'adapter à votre situation particulière que celle 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 avec 20 éléments dans votre ensemble - 20 C 3 = 1140. Et si vous voulez parcourir l'ensembles, il est préférable d'utiliser un gris modifié algorithme de code afin que vous ne les gardiez pas 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érentes utilisations. Voulons-nous maximiser les différences entre les combinaisons successives? minimiser? etc.
Certains des articles originaux décrivant les codes gris:
- Quelques chemins de Hamilton et un algorithme de changement minimal
- Algorithme de génération de combinaison d'échange adjacent
Voici quelques autres articles couvrant le sujet:
- Une implémentation efficace de l'algorithme Eades, Hickey, Read Adjacent Interchange Combination Generation (PDF, avec code en Pascal)
- Générateurs combinés
- Enquête sur les codes gris combinatoires (PostScript)
- Un algorithme pour les codes gris
Twiddle de Chase (algorithme)
Phillip J Chase, ` Algorithm 382: Combinations of M out of N Objects '(1970)
L'algorithme en C ...
Index des combinaisons dans l'ordre lexicographique (algorithme de boucles 515)
Vous pouvez également référencer une combinaison par son index (dans l'ordre lexicographique). En réalisant que l'indice devrait être une certaine quantité de changement de droite à gauche en fonction de l'indice, nous pouvons construire quelque chose qui devrait récupérer une combinaison.
Donc, nous avons 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 minime. {1, 2, 4} a un changement et est lexicographiquement numéro 2. Ainsi, le nombre de "changements" en dernier lieu explique un changement dans l'ordre lexicographique. La deuxième place, avec un changement {1,3,4} a un changement mais représente plus de changement car il est à la deuxième place (proportionnel au nombre d'éléments dans l'ensemble d'origine).
La méthode que j'ai décrite est une déconstruction, comme il semble, de l'ensemble à l'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 du C pour les calculer , avec des changements mineurs - j'ai utilisé l'index des ensembles plutôt qu'une plage de nombres pour représenter l'ensemble, donc nous travaillons toujours de 0 à n. Remarque:
- Les combinaisons n'étant pas ordonnées, {1,3,2} = {1,2,3} - nous les ordonnons lexicographiques.
- Cette méthode a un 0 implicite pour démarrer l'ensemble pour la première différence.
Index des combinaisons dans l'ordre lexicographique (McCaffrey)
Il y a une autre façon : son concept est plus facile à saisir et à programmer mais il est sans les optimisations de Buckles. Heureusement, il ne produit pas non plus de combinaisons en double:
L'ensemble qui maximise , où .
Pour un exemple: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
. Donc, la 27e combinaison lexicographique de quatre choses est: {1,2,5,6}, ce sont les index de tout ensemble que vous voulez regarder. Exemple ci-dessous (OCaml), nécessite une choose
fonction, laissé 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)
Un itérateur de combinaisons petites et simples
Les deux algorithmes suivants sont fournis à des fins didactiques. Ils implémentent un itérateur et (plus généralement) des combinaisons globales de dossiers. Ils sont aussi rapides que possible, ayant la complexité O ( n C k ). La consommation de mémoire est liée par k
.
Nous commencerons par l'itérateur, qui appellera une fonction fournie par l'utilisateur pour chaque combinaison
let iter_combs n k f =
let rec iter v s j =
if j = k then f v
else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
iter [] 0 0
Une version plus générale appellera la fonction fournie par l'utilisateur avec la variable d'état, à partir de l'état initial. Étant donné que nous devons passer l'état entre différents états, nous n'utiliserons pas la boucle for, mais à la place, utilisez la récursivité,
let fold_combs n k f x =
let rec loop i s c x =
if i < n then
loop (i+1) s c @@
let c = i::c and s = s + 1 and i = i + 1 in
if s < k then loop i s c x else f c x
else x in
loop 0 0 [] x