HOWTO · C++

Vector de embaralhamento em C++

Este artigo introduz como baralhar um vector em C++

Nesta página

Para reordenar aleatoriamente os elementos de um vetor C++ no próprio lugar, chame std::shuffle com os iteradores begin() e end() do vetor e um mecanismo de geração de números aleatórios. O algoritmo reorganiza os elementos do intervalo fornecido; ele não cria um segundo vetor. Use uma semente fixa para o mecanismo quando quiser repetir a sequência de escolhas pseudoaleatórias, ou inicialize-o com uma fonte apropriada quando quiser que execuções diferentes geralmente produzam sequências distintas.

Embaralhar um vetor com std::shuffle

std::shuffle é declarado no cabeçalho <algorithm>. Seus dois primeiros argumentos delimitam um intervalo semiaberto de iteradores: o primeiro aponta para o elemento inicial incluído, e o segundo aponta para a posição logo após o último elemento. Portanto, em um vetor, values.begin() e values.end() abrangem o vetor inteiro. O terceiro argumento é um UniformRandomBitGenerator (gerador uniforme de bits aleatórios), normalmente um mecanismo como std::mt19937. Esse mecanismo fornece os bits aleatórios usados para escolher a permutação; ele não é uma função de retorno que entrega um vetor já embaralhado.

Este exemplo completo usa uma semente fixa para que o mecanismo comece no mesmo estado a cada execução do programa. A ordem específica resultante não é garantida por std::shuffle: ela pode variar conforme a implementação da biblioteca padrão. Portanto, considere a saída abaixo um exemplo, e não uma sequência garantida.

Execute este exemplo:

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

O vetor de entrada é 12 32 43 53 23 65 84. Na execução bem-sucedida fornecida, o programa exibiu a ordem embaralhada mostrada abaixo:

23 12 43 53 84 65 32

Uma semente fixa torna reproduzível a sequência do mecanismo pseudoaleatório na mesma implementação e configuração. Ela não garante uma saída embaralhada idêntica em todas as implementações de bibliotecas padrão, pois as escolhas exatas do algoritmo não são definidas como uma sequência universal. Para obter variação entre execuções, inicialize o mecanismo uma vez e reutilize-o nas operações de embaralhamento, em vez de criar um mecanismo novo para cada elemento ou chamada. std::random_device é usado com frequência como fonte de sementes, mas seu comportamento depende da implementação e do ambiente; não deve ser apresentado como garantia de não determinismo ou de aleatoriedade criptográfica.

Também é possível expressar o intervalo com std::begin(values) e std::end(values), disponíveis pelos utilitários padrão para iteradores. Essa forma é útil ao escrever uma função genérica que aceita diferentes tipos de contêiner ou arrays nativos. Em uma chamada específica para um vetor, os iteradores membros são diretos e claros. Em qualquer uma das formas, passe as duas extremidades do mesmo contêiner e confira se o intervalo contém os elementos que você pretende permutar. A operação altera a ordem do vetor no próprio lugar; assim, o código que o ler ou percorrer depois verá a disposição embaralhada.

Lidar com código legado que usa std::random_shuffle

std::random_shuffle é um algoritmo mais antigo da biblioteca padrão que pode aparecer em código C++ existente. Sua disponibilidade depende do padrão da linguagem selecionado pelo projeto e da implementação da biblioteca usada. Ao manter esse código, confira essas configurações antes de presumir que o nome está disponível. As sobrecargas mais antigas podem usar uma fonte de aleatoriedade definida pela implementação, enquanto as sobrecargas que aceitam um gerador têm requisitos diferentes dos do argumento de mecanismo usado por std::shuffle.

Em código novo ou atualizado, prefira std::shuffle e passe um mecanismo aleatório explícito. Assim, a fonte de aleatoriedade fica visível, você pode controlar a repetibilidade com uma semente e evita depender da interface antiga. A presença de std::random_shuffle em uma base de código não significa que ele esteja disponível em todos os modos do compilador; tampouco significa que std::shuffle esteja ausente em todo ambiente mais antigo. A compatibilidade depende da configuração do padrão da linguagem e da biblioteca padrão do projeto. Se o projeto precisar manter código legado, limite essa escolha de compatibilidade ao próprio projeto e confira-a com a configuração do compilador usada na compilação.