HOWTO · C++

Shuffle Vector in C++

Questo articolo introduce come mescolare un vettore in C++

In questa pagina

Per riordinare casualmente gli elementi di un vector C++ direttamente sul posto, chiama std::shuffle passando gli iteratori begin() e end() del vector e un motore per la generazione di numeri casuali. L’algoritmo riordina gli elementi dell’intervallo specificato; non crea un secondo vector. Usa un seed fisso per il motore quando vuoi ottenere una sequenza ripetibile di scelte pseudo-casuali, oppure inizializzalo con una sorgente adeguata se vuoi che esecuzioni diverse producano di norma sequenze diverse.

Mescolare un vector con std::shuffle

std::shuffle è dichiarato nell’header <algorithm>. I primi due argomenti delimitano un intervallo di iteratori semiaperto: il primo iteratore punta al primo elemento incluso, mentre il secondo punta subito dopo l’ultimo. Per un vector, values.begin() e values.end() comprendono quindi tutti gli elementi. Il terzo argomento è un generatore uniforme di bit casuali (UniformRandomBitGenerator), spesso un motore come std::mt19937. Il motore fornisce i bit casuali usati per scegliere la permutazione; non è una funzione di callback che restituisce un vector già mescolato.

Questo esempio completo usa un seed fisso, così il motore parte ogni volta dallo stesso stato quando il programma viene eseguito. std::shuffle non garantisce uno specifico ordine degli elementi: il risultato può variare in base all’implementazione della libreria standard. Considera quindi l’output seguente un esempio, non una sequenza garantita.

Esegui questo esempio:

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <random>
#include <vector>

int main() {
    std::vector<int> values{12, 32, 43, 53, 23, 65, 84};
    std::mt19937 engine(42);

    std::shuffle(values.begin(), values.end(), engine);

    for (std::size_t i = 0; i < values.size(); ++i) {
        if (i != 0) {
            std::cout << ' ';
        }
        std::cout << values[i];
    }
    std::cout << '\n';
}

Il vector di input è 12 32 43 53 23 65 84. Nell’esecuzione riuscita fornita, il programma ha stampato il seguente ordine mescolato:

23 12 43 53 84 65 32

Un seed fisso rende ripetibile la sequenza del motore pseudo-casuale con la stessa implementazione e configurazione. Non garantisce che l’output del mescolamento sia identico con tutte le implementazioni della libreria standard, perché l’algoritmo non definisce un’unica sequenza universale di scelte. Per ottenere risultati variabili, inizializza il motore una volta e riutilizzalo per più mescolamenti, invece di crearne uno nuovo per ogni elemento o per ogni chiamata. std::random_device viene spesso usato come sorgente per il seed, ma il suo comportamento dipende dall’implementazione e dall’ambiente: non garantisce né risultati sempre non deterministici né casualità crittografica.

Puoi anche specificare l’intervallo con std::begin(values) e std::end(values), disponibili tramite le utility standard per gli iteratori. Questa forma è utile quando scrivi una funzione generica che accetta tipi diversi di contenitore o array nativi. Per una chiamata specifica su un vector, gli iteratori membro sono diretti e chiari. In entrambi i casi, passa gli estremi dello stesso contenitore e assicurati che l’intervallo includa gli elementi che vuoi permutare. L’operazione cambia l’ordine del vector sul posto: il codice che lo legge o lo scorre in seguito vedrà la disposizione mescolata.

Gestire il codice legacy che usa std::random_shuffle

std::random_shuffle è un algoritmo più datato della libreria standard che può comparire nel codice C++ esistente. La sua disponibilità dipende dallo standard del linguaggio selezionato dal progetto e dall’implementazione della libreria in uso. Quando mantieni codice di questo tipo, verifica queste impostazioni prima di presumere che il nome sia disponibile. Gli overload più vecchi possono usare una sorgente di casualità definita dall’implementazione; gli overload che accettano un generatore hanno invece requisiti diversi da quelli del motore passato a std::shuffle.

Nel codice nuovo o aggiornato, preferisci std::shuffle e passa un motore casuale esplicito. In questo modo la sorgente di casualità è visibile, puoi controllare la ripetibilità tramite un seed e non dipendi dall’interfaccia più datata. La presenza di std::random_shuffle nel codice non significa che sia disponibile in ogni modalità del compilatore, così come un ambiente meno recente non è necessariamente privo di std::shuffle: la compatibilità dipende dalla configurazione dello standard del linguaggio e della libreria del progetto. Se devi mantenere codice legacy, limita la scelta di compatibilità al progetto interessato e verificala con la configurazione del compilatore usata per compilarlo.