使用#pragma omp进行硬并行化以查找第N个素数

时间:2012-11-22 21:33:19

标签: c multithreading parallel-processing openmp primes

这个问题的目的是能够获得2.000.000的第一个素数,并且能够分辨出2.000.000的素数。

我们从这段代码开始:

#include <stdlib.h>
#include <stdio.h>

#define N 2000000

int p[N];

main(int na,char* arg[])
{
int i;
int pp,num;

printf("Number of primes to find: %d\n",N);

p[0] = 2;
p[1] = 3;
pp = 2;
num = 5;

while (pp < N)
{
  for (i=1; p[i]*p[i] <= num ;i++)
    if (num % p[i] == 0) break;
  if (p[i]*p[i] > num) p[pp++]=num;
  num += 2;
}

printf("The %d prime is: %d\n",N,p[N-1]);
exit(0);
}

现在我们被要求通过pragma omp进行此过程。这就是我到目前为止所做的:

#include <stdlib.h>
#include <stdio.h>

#define N 2000000
#define D 1415

int p[N];
main(int na,char* arg[])
{
int i,j;
int pp,num;

printf("Number of primes to find: %d\n",N);

p[0] = 2;
p[1] = 3;

pp = 2;
num = 5;

while (pp < D)
{
    for (i=1; p[i]*p[i] <= num ;i++)
        if (num % p[i] == 0) break;
    if (p[i]*p[i] > num) p[pp++]=num;
    num += 2;
}

int success = 0;
int t_num;
int temp_num = num;
int total = pp;

#pragma omp parallel num_threads(4) private(j, t_num, num, success)
{
    t_num = omp_get_thread_num();
    num = temp_num + t_num*2;

    #pragma omp for ordered schedule(static,4)
    for(pp=D; pp<N; pp++) {
        success = 0;
        while(success==0) {
            for (i=1; p[i]*p[i] <= num;i++) {
                if (num % p[i] == 0) break;
            }
            if (p[i]*p[i] > num) {
                p[pp] = num;
                success=1;
            }
            num+=8;
        }

    }
}

//sort(p, 0, N);

printf("El %d primer es: %d\n",N,p[N-1]);

exit(0);
}

现在让我解释一下我的“部分”解决方案,以及我的问题。

使用顺序代码获得第一个D素数,所以现在我可以检查大量数字的可分性。

每个线程都运行一个素数对角线,这样线程之间就没有依赖关系,也就不需要同步了。但是,这种方法的问题如下:

  1. 一个线程可能会生成比另一个线程更多的素数
  2. 作为问题1的直接后果,它将产生N个素数,但它们不会被有序,所以当素数计数器'pp'达到'N'时,最后的素数不是2.000.000的素数,它是一个更高级的素数。
  3. 也可能是当它产生2.000.000个素数时,能够达到真正的第2000个素数的线程可能没有时间将它放在素数数组'p'上。
  4. 问题/困境是:

    我怎么能知道何时生成了第2.000.000个素数?

    提示: 我被告知我应该批量(假设)10.000个素数候选人。然后,当我不知道的事情发生时,我会知道最后一批10.000候选人包含2.000.000th prime,我可以用quicksort对它进行排序。

    我希望自己明确表示,这是非常好的运动,我只是试了好几天。

2 个答案:

答案 0 :(得分:2)

如果您只需要2000000个素数,则可以为每个找到的素数维持一个~4.1MB大小的比特阵列和翻转位。不需要排序。通过实现仅赔率表示方案将您的比特阵列大小减半。

在段中使用Sieve of Eratosthenes,其大小与sqrt(top_value_of_range)成比例(或类似的东西 - 目标是在每个细分上执行大致相同的工作量)。对于n=2000000n*(log n + log(log n)) == 34366806prime[771]^2 == 34421689(从0开始),因此,预先计算前771个素数。

每个工作人员也可以计算,因为它会翻转这些位,因此当它们全部完成时你将知道每个范围的计数,并且只需要扫描包含第2个第2个素数的一个范围。结束,找到那个素数。或者让每个工作人员根据其范围保持自己的比特阵列 - 你只需要保留一个,并且可以丢弃其他人。

计算Eratosthenes筛子的伪代码是:

Input: an integer n > 1

Let A be an array of bool values, indexed by integers 3, 5, ... upto n,
initially all set to true.

count := floor( (n-1)/2 )
for i = 3, 5, 7, ..., while i^2 ≤ n:
  if A[i] is true:
    for j = i^2, i^2 + 2i, i^2 + 4i, ..., while j ≤ n:
      if A[j] is true:
        A[j]  := false
        count := count - 1

Now all 'i's such that A[i] is true are prime,
and 'count' is the total count of odd primes found.

答案 1 :(得分:1)

我可以想到两种方法。

  1. 一旦你有了第二百万个素数的候选人,你的线程会继续计算低于你的候选人的素数,直到你没有素数丢失。然后你可以对素数列表进行排序,并从中获取第二百万分之一。

  2. 如果您的线程正在生成顺序素数块,它们应该分别维护块,然后可以将素数块重新组合成主列表。执行重组的线程可以在程序找到第2百万个素数后终止该程序。