web-dev-qa-db-fra.com

Comment puis-je générer des nombres aléatoires de tri uniformément distribué de manière efficace en C ++?

Je voudrais générer un grand nombre, n, (c'est-à-dire n >= 1,000,000,000) des nombres aléatoires triés et uniformément distribués en C++.

A d'abord et une approche simple que j'ai considérée était de

  1. générer séquentiellement n nombres uniformément distribués à l'aide d'un std::uniform_real_distribution<double>,
  2. puis triez-les en utilisant std::sort.

Cependant, cela prend plusieurs minutes.

A Deuxièmement et une approche plus sophistiquée consistait à faire paralléliser les deux étapes comme dans:

template <typename T>
void computeUniformDistribution(std::vector<T>& elements)
{
    #pragma omp parallel
    {
        std::seed_seq seed{distribution_seed, static_cast<size_t>(omp_get_thread_num())};
        std::mt19937 prng = std::mt19937(seed);
        std::uniform_real_distribution<double> uniform_dist(0, std::numeric_limits<T>::max());

        #pragma omp for
        for (size_t i = 0; i < elements.size(); ++i)
        {
            elements[i] = static_cast<T>(uniform_dist(prng));
        }
    }

    std::sort(std::execution::par_unseq, elements.begin(), elements.end());
}

Cependant, même cela prend environ 30 secondes. Étant donné que la génération des nombres uniformément distribués prend uniquement environ 1.5 secondes, le goulot d'étranglement reste la phase de tri.

Par conséquent, j'aimerais poser la question suivante: Comment puis-je générer efficacement des données uniformément distribuées de manière triée?

30
epic-skyrise-tm

Il existe une simple observation impliquant des nombres aléatoires uniformes triés dans [0, 1]:

  1. Chaque nombre uniforme [0, 1] est également susceptible d'être inférieur à la moitié ou supérieur à la moitié. Ainsi, le nombre d'uniformes [0, 1] numéros inférieurs à la moitié vs supérieur à la moitié suit une distribution binomiale (N, 1/2).
  2. Des chiffres inférieurs à la moitié, chaque nombre est aussi susceptible d'être inférieur à 1/4, car il doit être supérieur à 1/4, de sorte que les nombres moins de 1/4 vs supérieurs à 1/4 vs. Suivez la même distribution.
  3. Etc.

Ainsi, chaque numéro peut être généré un bit à la fois, de gauche à droite après le point binaire. Voici un croquis de la manière dont cela fonctionne pour générer N Tri des nombres aléatoires uniformes:

  1. Si N est 0 ou 1, arrêtez-vous. Sinon, générez B, un binôme ( N, 1/2) Nombre aléatoire.
  2. Ajouter 0 au premier B nombre aléatoire et 1 au reste.
  3. Exécutez cet algorithme récursivement sur le premier B numéros, mais avec N = B.
  4. Exécutez cet algorithme récursivement sur le reste des chiffres, mais avec N = N - B.

À ce stade, nous avons une liste triée de nombres aléatoires avec des comptes de bits variés. Tout ce qui reste à faire est de remplir chaque nombre avec des bits aléatoires uniformes au besoin (ou de couper les bits d'excès ronds) pour donner le numéro P bits (par exemple 53 bits pour la double précision) . Ensuite, divisez chaque numéro par 2 P.

Je donne un algorithme similaire Pour trouver le k - Le plus petit de N Numéros aléatoires.

2
Peter O.