Art of Computer Programming Volume 4: Fascicle 3 ha un sacco di questi che potrebbero adattarsi alla tua situazione particolare meglio di come descrivo.
Codici grigi
Un problema che incontrerai è ovviamente la memoria e abbastanza rapidamente, avrai problemi con 20 elementi nel tuo set - 20 C 3 = 1140. E se vuoi iterare sul set è meglio usare un grigio modificato algoritmo di codice in modo da non tenerli tutti in memoria. Questi generano la combinazione successiva dalla precedente ed evitano le ripetizioni. Ce ne sono molti per usi diversi. Vogliamo massimizzare le differenze tra combinazioni successive? minimizzare? eccetera.
Alcuni dei documenti originali che descrivono i codici grigi:
- Alcuni percorsi Hamilton e un algoritmo di cambiamento minimo
- Algoritmo di generazione della combinazione di interscambio adiacente
Ecco alcuni altri articoli che trattano l'argomento:
- Un'attuazione efficiente dell'algoritmo di generazione di combinazioni di interscambio adiacente Eades, Hickey, Read Adiacente (PDF, con codice in Pascal)
- Generatori di combinazioni
- Rilievo di codici grigi combinatori (PostScript)
- Un algoritmo per i codici grigi
Chase's Twiddle (algoritmo)
Phillip J Chase, ` Algorithm 382: Combinations of M out of N Objects '(1970)
L'algoritmo in C ...
Indice delle combinazioni nell'ordine lessicografico (Buckles Algorithm 515)
Puoi anche fare riferimento a una combinazione in base al suo indice (in ordine lessicografico). Comprendendo che l'indice dovrebbe essere una certa quantità di cambiamento da destra a sinistra in base all'indice, possiamo costruire qualcosa che dovrebbe recuperare una combinazione.
Quindi, abbiamo un set {1,2,3,4,5,6} ... e vogliamo tre elementi. Diciamo {1,2,3} possiamo dire che la differenza tra gli elementi è una e in ordine e minima. {1,2,4} ha una modifica ed è lessicograficamente il numero 2. Quindi il numero di "modifiche" all'ultimo posto rappresenta una modifica nell'ordinamento lessicografico. Il secondo posto, con una modifica {1,3,4} ha una modifica, ma tiene conto di altre modifiche poiché è al secondo posto (proporzionale al numero di elementi nel set originale).
Il metodo che ho descritto è una decostruzione, a quanto pare, dall'insieme all'indice, dobbiamo fare il contrario - il che è molto più complicato. Ecco come Buckles risolve il problema. Ho scritto alcune C per calcolarle , con piccole modifiche - ho usato l'indice degli insiemi piuttosto che un intervallo di numeri per rappresentare l'insieme, quindi lavoriamo sempre da 0 ... n. Nota:
- Poiché le combinazioni non sono ordinate, {1,3,2} = {1,2,3} - ordiniamo che siano lessicografiche.
- Questo metodo ha uno 0 implicito per avviare il set per la prima differenza.
Indice delle combinazioni nell'ordine lessicografico (McCaffrey)
C'è un altro modo : il suo concetto è più facile da capire e programmare ma è senza le ottimizzazioni di Buckles. Fortunatamente, non produce anche combinazioni duplicate:
L'insieme che massimizza , dove .
Per un esempio: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
. Quindi, la 27a combinazione lessicografica di quattro cose è: {1,2,5,6}, questi sono gli indici di qualunque set tu voglia guardare. Esempio sotto (OCaml), richiede choose
funzione, lasciato al lettore:
(* 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 iteratore di combinazioni piccole e semplici
I seguenti due algoritmi sono forniti a scopo didattico. Implementano combinazioni iteratrici e (più generali) di cartelle. Sono il più veloci possibile, con la complessità O ( n C k ). Il consumo di memoria è limitato da k
.
Inizieremo con l'iteratore, che chiamerà una funzione fornita dall'utente per ogni combinazione
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
Una versione più generale chiamerà la funzione fornita dall'utente insieme alla variabile di stato, a partire dallo stato iniziale. Dato che dobbiamo passare lo stato tra stati diversi, non useremo il ciclo for, ma utilizzeremo invece la ricorsione,
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