¿Cómo podemos escribir un código eficiente para determinar números primos hasta un valor dado, de modo que el límite de tiempo para cada caso de prueba no exceda un segundo en lenguaje C?

Este esquema implementa una versión paralela de Sieve of Eratosthenes.

Utiliza un conjunto de números candidatos n con valor máximo m, y una matriz tamaño-m que contiene datos de primalidad, indexados por el número candidato, cuyo espacio contiene “verdadero / falso / desconocido”. Inicialmente, todas las ranuras de la matriz están configuradas en “desconocido”.

También utiliza un grupo de subprocesos, con un número de subprocesos igual a 2 veces el número de CPU.

El bucle principal genera valores candidatos (n); comienza un hilo computacional con cada valor candidato. El subproceso computacional intenta factorizar su valor candidato por cualquier medio que desee (podría intentar valores divisores para ranuras de matriz i <n (mejor: i <sqrt (n)) que están marcadas como "verdaderas", haciendo un bucle en valores "desconocidos" [dándose cuenta de la versión de un pobre hombre de un valor "futuro"]). Para cada hilo computacional, el bucle principal también lanza otro hilo, dada la ID del hilo computacional para esperar un temporizador de 1 segundo.

Si el subproceso computacional determina un resultado, actualiza su ranura de matriz correspondiente, aborta el subproceso de un segundo y vuelve a colocar ambos subprocesos en el grupo. Si el subproceso del temporizador de un segundo se activa, simplemente aborta su subproceso computacional correspondiente y se coloca el hilo computacional en el grupo.

Un bit complicado es saber si el hilo computacional está abortando el segundo hilo o viceversa; no puedes hacer que ambos se aborten en el mismo instante. Para manejar esto, agregamos una variable de bloqueo paralelo para cada índice que ambos hilos intentan adquirir antes de abortar el otro hilo.

Un segundo bit complicado es asegurarse de que el almacenamiento utilizado por cada hilo no se pierda en un aborto; para un cálculo tan simple como factorizar primos, es probable que no necesite ninguna asignación de almacenamiento dinámico, y las variables locales probablemente estén en una “gran pila contigua” propiedad del subproceso y, por lo tanto, se reciclarán cuando el subproceso se recicle.

Una vez que el bucle principal ha generado todos los valores, espera a que el grupo de subprocesos se vuelva a llenar a su capacidad máxima. La matriz contiene las respuestas.

Todo esto supone actualizaciones de ranuras de matriz atómica (típicas del hardware moderno) y un sistema operativo (o su código) que proporciona operaciones para start-thread-from-pool, temporizadores basados ​​en hilos específicos, bloqueos para hilos y un comando de aborto de hilos. La mayoría de los sistemas operativos tienen todas estas primitivas disponibles.

Los detalles de codificación para C se dejan al lector.

Dudo que esto sea eficiente para pequeños m. Es probable que el esfuerzo para lanzar un hilo desde el grupo sea bastante alto. Para grandes m, el costo de factorizar podría dominar los tiempos de lanzamiento de subprocesos, por lo que podría ser eficiente allí. Dado un valor máximo de m = 2 ^ 32, la factorización debe intentar ingenuamente los valores de sqrt (2 ^ 32) ~~ 2 ^ 16 a 10 ns por división (velocidades de hardware típicas hoy) que dan 650K ns o .0065 segundos de trabajo computacional, lo que significa los temporizadores de 1 segundo nunca se activan, por lo que es mejor que los omita. Claramente no quieres establecer m = 2 ^ 64; Su matriz será increíblemente grande.

Solo por diversión … en nuestro lenguaje paralelo, PARLANSE, sin los temporizadores de 1 segundo, esto se ve así:

(definir principal
(acción (procedimiento nulo)
(local (;; [is_prime (array (evento booleano) 1 dinámico)]
(= [máx. natural] 123456789)
[equipo de trabajadores]
) ;;
(resize is_prime 1 max) resize; tenga en cuenta que los pares de apertura y cierre pueden tener “color”
(do (= [n natural] 1) max 1; enumerar valores para intentar
(consumir (reclutar trabajadores; agregar nuevos trabajadores al equipo de trabajadores
(acción (procedimiento [candidato natural]); la acción es como lambda sin valor
(hacer [divisor natural] 1 (- m 1) 1; enumerar divisores
(ifthen (&& isprime: divisor_index
(== (módulo candidato divisor) 0)
) &&
(;; (= is_prime: candidato ~ f); señales de disponibilidad
(regreso) ; divisible -> no es primo
) ;;
) ifthen
)hacer
(= is_prime: candidato ~ t); no divisible por ninguno -> primo
)acción
norte
)consumir
)hacer
(espera (trabajadores del evento)); espera a que todos los miembros del equipo completen
)local
)acción
)definir

La sintaxis es obviamente similar a Lisp, pero no es Lisp. La matriz es una matriz de “futuros” (llamados “eventos”) en PARLANSE), todos inicialmente armados como “aún no ha llegado ningún valor”. La asignación de is_prime: candidato señala el evento; cualquier lector de un evento está obligado a esperar hasta se ha señalado. La acción “reclutar” es donde ocurre el paralelismo; se lanza un grano de cómputo que consiste en la acción interna (un procedimiento con un argumento entero positivo) en cada valor candidato. Cada grano candidato se “recluta” (agregado) Nota: el equipo actúa como un límite natural en la computación paralela; PARLANSE genera suficientes reclutas para mantener a los procesadores más que ocupados, y luego invierte en la ejecución de los reclutas existentes.

Primero quiero borrar la restricción de tiempo que presentó. Si tiene esta limitación de tiempo debido a alguna pregunta en línea o por cualquier otra razón. El lenguaje C puede calcular 10ˆ8 cálculos en un promedio. Puede convertirse en 10ˆ9 si los cálculos son muy simples, pero para estar seguros consideramos que ocurren 10ˆ8. Entonces, hay un límite máximo para determinar los números primos.

Uno de los algoritmos más famosos para encontrar números primos hasta cierto número es Sieve of Eratosthenes – Wikipedia. En el enlace dado, explica todo el proceso con gran detalle. La versión más simple tiene una complejidad temporal de O (n log log n) donde n será el número primo más grande que se permitirá calcular. Hay otras variantes de este algoritmo que lo llevan a una complejidad temporal casi lineal.

Tengo mi código personal del cual da una complejidad casi lineal. Lo siento, no sé la complejidad exacta de tiempo que lleva.

const int N = 10000000;
int lp [N + 1];
vector pr;

para (int i = 2; i <= N; ++ i) {
if (lp [i] == 0) {
lp [i] = i;
pr.push_back (i);
}
para (int j = 0; j <(int) pr.size () && pr [j] <= lp [i] && i * pr [j] <= N; ++ j)
lp [i * pr [j]] = pr [j];
}

Aquí N serán los números máximos para pasar y obtener todos los números primos hasta ese número. Está en C ++ pero puedes entenderlo y modificarlo fácilmente. la fuente del código anterior es de un sitio web ruso: Решето Эратосфена с линейным временем работы

More Interesting

Big data, seguridad informática y matemática financiera; ¿Cuál de estos campos es el mejor para emprender como carrera si eres de antecedentes matemáticos?

¿Puedo ser un buen ingeniero informático si mi matemática es débil?

Teoría estadística: ¿Cuáles son algunos resultados teóricos interesantes relacionados con la Estimación de la densidad del núcleo?

Cómo aprender sobre qué pueden hacer las matemáticas en una computadora

¿Cuáles son algunas aplicaciones interesantes de las matemáticas en la vida real?

¿Es la arquitectura de las computadoras de Von Neumann, se basó en su trabajo ... o fue alguien más?

¿Cuál es el límite y cómo puedo probar: [math] \ lim_ {n \ rightarrow \ infty} {n ^ dn ^ c} [/ math] cuando [math] 0 \ leq c <d [/ math]?

¿Cuál es la banda de transición máxima permitida del filtro de paso bajo utilizado en el núcleo de reconstrucción para un CD con una frecuencia de voz máxima de 4 kHz?

¿Cuáles son algunas de las aplicaciones más elegantes de la teoría de grafos?

¿Pueden los lenguajes naturales ser completamente modelados por las máquinas de Turing?

¿P = NP sería algo bueno?

Alguien me dijo que me especializara en un dominio CS para evitar quedar desempleado cuando envejeciera, ¿es cierto?

Si el punto (3, -4) divide la línea entre el eje x y el eje y en la relación 2: 3, ¿cuál será la ecuación lineal?

¿Podría la programación de aprendizaje y las matemáticas cambiar mis patrones de pensamiento?

¿Cuáles son los algoritmos que debo aprender para comenzar a estudiar la inteligencia artificial?