Compartir a través de


Cómo: Usar contenedores paralelos para aumentar la eficacia

En este tema se muestra cómo se usan contenedores paralelos para almacenar de forma eficaz los datos y tener acceso a ellos en paralelo.

En el código de ejemplo se calcula el conjunto de números primos y de números de Carmichael en paralelo. A continuación, el código calcula los factores primos de cada número de Carmichael.

Ejemplo: Determinar si un valor de entrada es un número primo

En el ejemplo siguiente se muestra la función is_prime, que determina si un valor de entrada es un número primo, y la función is_carmichael, que determina si el valor de entrada es un número de Carmichael.

// Determines whether the input value is prime.
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

// Determines whether the input value is a Carmichael number.
bool is_carmichael(const int n) 
{
   if (n < 2) 
      return false;

   int k = n;
   for (int i = 2; i <= k / i; ++i) 
   {
      if (k % i == 0) 
      {
         if ((k / i) % i == 0) 
            return false;
         if ((n - 1) % (i - 1) != 0)
            return false;
         k /= i;
         i = 1;
      }
   }
   return k != n && (n - 1) % (k - 1) == 0;
}

Ejemplo: Calcular números primos y de Carmichael

En el siguiente ejemplo se usan las funciones is_prime e is_carmichael para calcular los conjuntos de números primos y números de Carmichael. En el ejemplo se usan los algoritmos concurrency::parallel_invoke y concurrency::parallel_for para calcular cada conjunto en paralelo. Para obtener más información sobre los algoritmos en paralelo, vea Algoritmos paralelos.

En este ejemplo se usa un objeto concurrency::concurrent_queue para almacenar el conjunto de números de Carmichael porque más adelante se usará ese objeto como una cola de trabajo. Se usa un objeto concurrency::concurrent_vector para almacenar el conjunto de números primos porque más adelante se recorrerá en iteración este conjunto para buscar factores primos.

// The maximum number to test.
const int max = 10000000;

// Holds the Carmichael numbers that are in the range [0, max).
concurrent_queue<int> carmichaels;

// Holds the prime numbers that are in the range  [0, sqrt(max)).
concurrent_vector<int> primes;

// Generate the set of Carmichael numbers and the set of prime numbers
// in parallel.
parallel_invoke(
   [&] {
      parallel_for(0, max, [&](int i) {
         if (is_carmichael(i)) {
            carmichaels.push(i);
         }
      });
   },
   [&] {
      parallel_for(0, int(sqrt(static_cast<double>(max))), [&](int i) {
         if (is_prime(i)) {
            primes.push_back(i);
         }
      });
   });

Ejemplo: Buscar todos los factores primos de un valor determinado

En el siguiente ejemplo se muestra la función prime_factors_of, que usa la división por tentativa para encontrar todos los factores primos del valor especificado.

En esta función se usa el algoritmo concurrency::parallel_for_each para recorrer en iteración la colección de números primos. El objeto concurrent_vector permite al bucle paralelo agregar simultáneamente los factores primos al resultado.

// Finds all prime factors of the given value.
concurrent_vector<int> prime_factors_of(int n, 
   const concurrent_vector<int>& primes)
{
   // Holds the prime factors of n.
   concurrent_vector<int> prime_factors;
   
   // Use trial division to find the prime factors of n.
   // Every prime number that divides evenly into n is a prime factor of n.
   const int max = sqrt(static_cast<double>(n));
   parallel_for_each(begin(primes), end(primes), [&](int prime)
   {
      if (prime <= max)
      {         
         if ((n % prime) == 0)
            prime_factors.push_back(prime);
      }
   });

   return prime_factors;
}

Ejemplo: Procesa cada elemento de la cola de números de Carmichael

En este ejemplo se procesa cada elemento de la cola de números de Carmichael llamando a la función prime_factors_of para calcular sus factores primos. En el ejemplo se usa un grupo de tareas para realizar este trabajo en paralelo. Para obtener más información sobre los grupos de tareas, vea Paralelismo de tareas.

En este ejemplo se imprimen los factores primos de cada número de Carmichael si dicho número tiene más de cuatro factores primos.

// Use a task group to compute the prime factors of each 
// Carmichael number in parallel.
task_group tasks;

int carmichael;
while (carmichaels.try_pop(carmichael))
{
   tasks.run([carmichael,&primes] 
   {
      // Compute the prime factors.
      auto prime_factors = prime_factors_of(carmichael, primes);

      // For brevity, print the prime factors for the current number only
      // if there are more than 4.
      if (prime_factors.size() > 4)
      {
         // Sort and then print the prime factors.
         sort(begin(prime_factors), end(prime_factors));

         wstringstream ss;
         ss << L"Prime factors of " << carmichael << L" are:";

         for_each (begin(prime_factors), end(prime_factors), 
            [&](int prime_factor) { ss << L' ' << prime_factor; });
         ss << L'.' << endl;

         wcout << ss.str();
      }
   });
}

// Wait for the task group to finish.
tasks.wait();

Ejemplo: Ejemplo de código de contenedor paralelo terminado

En el código siguiente se muestra el ejemplo completo, en el que se usan los contenedores paralelos para calcular los factores primos de los números de Carmichael.

// carmichael-primes.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_queue.h>
#include <concurrent_vector.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Determines whether the input value is prime.
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

// Determines whether the input value is a Carmichael number.
bool is_carmichael(const int n) 
{
   if (n < 2) 
      return false;

   int k = n;
   for (int i = 2; i <= k / i; ++i) 
   {
      if (k % i == 0) 
      {
         if ((k / i) % i == 0) 
            return false;
         if ((n - 1) % (i - 1) != 0)
            return false;
         k /= i;
         i = 1;
      }
   }
   return k != n && (n - 1) % (k - 1) == 0;
}

// Finds all prime factors of the given value.
concurrent_vector<int> prime_factors_of(int n, 
   const concurrent_vector<int>& primes)
{
   // Holds the prime factors of n.
   concurrent_vector<int> prime_factors;
   
   // Use trial division to find the prime factors of n.
   // Every prime number that divides evenly into n is a prime factor of n.
   const int max = sqrt(static_cast<double>(n));
   parallel_for_each(begin(primes), end(primes), [&](int prime)
   {
      if (prime <= max)
      {         
         if ((n % prime) == 0)
            prime_factors.push_back(prime);
      }
   });

   return prime_factors;
}

int wmain()
{
   // The maximum number to test.
   const int max = 10000000;
   
   // Holds the Carmichael numbers that are in the range [0, max).
   concurrent_queue<int> carmichaels;

   // Holds the prime numbers that are in the range  [0, sqrt(max)).
   concurrent_vector<int> primes;
   
   // Generate the set of Carmichael numbers and the set of prime numbers
   // in parallel.
   parallel_invoke(
      [&] {
         parallel_for(0, max, [&](int i) {
            if (is_carmichael(i)) {
               carmichaels.push(i);
            }
         });
      },
      [&] {
         parallel_for(0, int(sqrt(static_cast<double>(max))), [&](int i) {
            if (is_prime(i)) {
               primes.push_back(i);
            }
         });
      });

   // Use a task group to compute the prime factors of each 
   // Carmichael number in parallel.
   task_group tasks;

   int carmichael;
   while (carmichaels.try_pop(carmichael))
   {
      tasks.run([carmichael,&primes] 
      {
         // Compute the prime factors.
         auto prime_factors = prime_factors_of(carmichael, primes);

         // For brevity, print the prime factors for the current number only
         // if there are more than 4.
         if (prime_factors.size() > 4)
         {
            // Sort and then print the prime factors.
            sort(begin(prime_factors), end(prime_factors));

            wstringstream ss;
            ss << L"Prime factors of " << carmichael << L" are:";

            for_each (begin(prime_factors), end(prime_factors), 
               [&](int prime_factor) { ss << L' ' << prime_factor; });
            ss << L'.' << endl;

            wcout << ss.str();
         }
      });
   }

   // Wait for the task group to finish.
   tasks.wait();
}

Este ejemplo genera la siguiente salida de ejemplo.

Prime factors of 9890881 are: 7 11 13 41 241.
Prime factors of 825265 are: 5 7 17 19 73.
Prime factors of 1050985 are: 5 13 19 23 37.

Compilar el código

Copie el código de ejemplo y péguelo en un proyecto de Visual Studio o en un archivo denominado carmichael-primes.cpp y, después, ejecute el siguiente comando en una ventana del símbolo del sistema de Visual Studio.

cl.exe /EHsc carmichael-primes.cpp

Consulte también

Contenedores y objetos paralelos
Paralelismo de tareas
concurrent_vector (clase)
concurrent_queue (clase)
función parallel_invoke
función parallel_for
task_group (clase)