素数算法

时间:2011-01-26 19:30:14

标签: c algorithm

有谁能告诉我如何在C中实现Sieve of Eratosthenes算法?我需要生成素数但我的算法很慢。

我的代码:

#include <stdio.h>

int prime(long int i)
{
    long int j;
    int state = 1;
    for(j=2;j<i;j++)
    {
        if((i%j)==0){state=0;break;}
    }
    return state;
}

int main()
{
    int t;
    long int m,n,i;
    scanf("%d", &t);
    while(t--) {
        scanf("%d %d", &m,&n);
        for(i=m;i<=n;i++)
        {
            if(i==1){
                //do nothing for 1
            } else{
                if(prime(i))printf("%d\n",i);
            }
        }
    }
    return 0;
}

t是测试用例的数量m,n是要打印质数的范围。

7 个答案:

答案 0 :(得分:9)

您需要创建一个与您要查找的最大素数一样大的布尔数组。在开始时它完全初始化为真。

如果i是素数,则此类数组的i单元格将为true;如果不是,则为false。

i=2开始迭代:它是素数,然后将索引倍数为2的任何单元格设置为false。转到下一个素数(i=3)并执行相同操作。转到下一个素数(它是i=5i=4不是素数:array[4]在处理i=2时设置为false,并且一次又一次地执行相同操作。

答案 1 :(得分:6)

在我看来,你的算法很慢,因为你计算了不必要的数字。 试试这段代码

int isPrime(int number){

    if(number < 2) return 0;
    if(number == 2) return 1;
    if(number % 2 == 0) return 0;
    for(int i=3; (i*i)<=number; i+=2){
        if(number % i == 0 ) return 0;
    }
    return 1;

}

答案 2 :(得分:5)

这是使用Eratosthenes Sieve算法的非常简单的代码。适用于所有积极的int

int is_prime(int n){
  int p;
  for(p = 2; p < n; p++){
    if(n % p ==0 && p != n)
      return 0;    
  }
  return 1;
}

答案 3 :(得分:3)

Marc B的链接显示了一个非常简单的算法,该算法是正确的,由NSLogan编写。我对它进行了一些修改,以显示一些大小/速度权衡。为了感兴趣,我以为我会分享。

首先,代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <time.h>

#define USE_BITS

#ifdef USE_BITS
#define alloc_prime char *prime = calloc(i/8,sizeof(*prime));
#define set_not_prime(x) prime[x/8]|= (1<<(x%8))
#define is_prime(x) (!(prime[x/8]&(1<<(x%8))))
#endif

#ifdef USE_CHAR
#define alloc_prime char *prime = calloc(i,sizeof(*prime));
#define set_not_prime(x) prime[x] = 1
#define is_prime(x) (prime[x] == 0)
#endif

#ifdef USE_SIZE_TYPE
#define alloc_prime size_t *prime = calloc(i,sizeof(*prime));
#define set_not_prime(x) prime[x] = 1
#define is_prime(x) (prime[x] == 0)
#endif


int main(){
    int i;
    printf("Find primes up to: ");
    scanf("%i",&i);

    clock_t start, stop;
    double t = 0.0;

    assert((start = clock())!=-1);

    //create prime list
    alloc_prime;
    int c1, c2, c3;
    if(!prime){
    printf("Can't allocate %zu bytes!\n",i*sizeof(*prime));
    exit(1);
    }

    //set 0 and 1 as not prime
    set_not_prime(0);
    set_not_prime(1);

    //find primes then eliminate their multiples (0 = prime, 1 = composite)
    for(c2 = 2;c2 <= (int)sqrt(i)+1;c2++){
    if(is_prime(c2)){
        c1=c2;
        for(c3 = 2*c1;c3 <= i+1; c3 += c1){
        set_not_prime(c3);
        }
    }
    }

    stop = clock();
    t = (double) (stop-start)/CLOCKS_PER_SEC;

    //print primes
    for(c1 = 0; c1 < i+1; c1++){
        if(is_prime(c1))printf("%i\n",c1);
        //      if(prime[c1] == 0) printf("%i\n",c1);
    }
    printf("Run time: %f\n", t); //print time to find primes

    return 0;
}

(原谅错误信息,因为定义了USE_BITS时不准确)。

我比较了三种不同的存储peoro建议的布尔变量的方法。一种方法实际上使用位,第二种方法使用整个字节,最后一种方法使用整个机器字。关于哪个最快的天真猜测可能是机器字方法,因为每个标志/布尔值由您的机器更“自然地”处理。在我的机器上计算高达100,000,000的质数的时间如下:

比特:3.8s 字符:5.8s M字:10.8s

值得注意的是,即使是只用一位表示布尔值所需的所有丑陋的位移仍然总体上更快。我的猜想是缓存和引用位置似乎超过了额外的3条指令。另外,你最终使用n位机器字方法的1 / n内存!

答案 4 :(得分:1)

虽然它的帖子很老,但我尝试使用&#34; Eratosthenes筛选&#34; 算法生成素数。

#include <stdio.h>

#define NUM 8000        /* Prime Numbers in the Range.  'Range + 2' actually. */

int main()
{
  int a[NUM] = {0};         /* Array which is going to hold all the numbers */
  int i , j;

  /* initializing array from 2 to given number + 2, assuming the first prime number is 2 */
  for(i = 2,j=0; i < NUM+2, j<NUM; i++, j++) 
  {
    a[j] =i;
  }


  for(i = 0; i < NUM; i++ ) 
  {
    int num = a[i];

    /* If number is not 0 then only update the later array index. */
    if(num != 0) 
    {
      for (j = i+1; j < NUM; j++) 
      {
        if( (a[j]%num == 0) ) 
        {
            a[j]=0;
        }
      }
    }
  }


  for(i = 0; i < NUM; i++) 
  {
    /* Print all the non Zero *Prime numbers* */
    if(a[i] != 0) 
    {
      printf("%d \n", a[i]);
    }
  }

}

希望这会对某人有所帮助。

答案 5 :(得分:0)

第一步是认识到将其拆分为任意大小的块是微不足道的;并且您不需要每个数字都需要一个数组(或位域)。例如,如果您只关心100000到110000之间的数字,那么要将所有可被3整除的数字标记为“不是素数”,可以执行“ for( index = 3 * (100000+3-1)/3; index < 110000; index += 3) {”。举一个更灵活的例子:

    for( value = 2; value < sqrt( block_end_value-1); value++ ) {
        for( index = value  * (block_state_value+value -1)/value; index < block_end_value; index += value ) {
            mark_not_prime(index - block_state_value);
        }
    }

第二步是认识到您不需要关心每个数字(并且上面的for( value = 2; value < sqrt( block_end_value-1); value++)效率很低)。例如,如果您已经将可被2整除的数字标记为“不是素数”,那么就没有理由关心数字是否可被4、6或8整除;否则,无需担心。并且,如果您已经将可被3整除的数字标记为“不是素数”,则没有理由担心数字是否可被6、9或12整除; and ...本质上,您只想测试一个数字是否可以被另一个质数整除。这意味着要查找在100000至110000范围内的质数,您首先要查找在0至sqrt(110000)范围内的质数;如果要查找0到sqrt(110000)范围内的素数,则要查找0到sqrt(sqrt(110000))范围内的素数。和....

第三步是认识到可以通过复制重复图案来加速它。您可以创建一个2位模式(表示“被2整除”),并将这2位复制到各处。以同样的方式,您可以创建6位模式(表示“可以被2或3整除”),并将这6位复制到各处。以相同的方式,您可以创建39916800位的模式(表示“可以被2、3、4、5、6、7、8、9、10和11整除”),并将该39916800位的模式复制到各处。当然,没有什么可以阻止您预先生成模式并将其存储在程序的代码中。

第四步是认识到2的倍数太小而无法存储,通过不存储它们,可以将所有表和模式(以及任何已存储/预先生成的模式)的内存消耗减少一半。

通过结合上面的第三步和第四步;代表“可被2、3、4、5、6、7、8、9、10和11整除的预生成模式”将花费19958400比特,约2.38 MiB。该模式的第一部分本身也可用于查找从1到大于11的第一个素数(在本例中为1到13的数)的素数。

第五步,要认识到,如果已经有一个模式,则可以使用它来查找“ N = the next "not marked yet" prime number”,将现有模式复制N次,然后将N的倍数标记为“非素数”;并以更大的模式结束。例如;如果您有一个模式表示“可被2、3、4、5、6、7、8、9、10和11整除”,则跳过12(因为根据现有模式它不是素数);复制模式13次,然后将可被13整除的数字标记为“不是素数”,最后得到表示“可被2、3、4、5、6、7、8、9、10、11整除的模式” ,12和13”。

第六步是要认识到,一旦您有一个足够大的图案达到所需范围,就可以填写缺失的除数而无需复制。例如,如果您只希望质数在1到6227020800之间;那么您可以采用表示“可以被2、3、4、5、6、7、8、9、10、11、12和13整除的模式”,然后标记可以被质数除以14的数字变为6227020800为“不是素数”。

通过结合以上所有内容;如果要查找在1000000000到11000000000之间的质数;那么您首先要找到介于1到sqrt(11000000000)之间的质数;为此,您需要复制一个预先生成的模式并将其扩展,直到您拥有一个足够大的表来表示1到sqrt(11000000000)范围内的质数为止;然后复制该扩展模式并填写缺失的数字,以生成一个表,该表表示范围为1到sqrt(11000000000)的质数;然后为范围为1000000000至11000000000的质数创建一个表,并通过将数据从“扩展的预先生成的模式”复制到其中来初始化内存,然后使用范围为1至sqrt(11000000000)的质数表来查找尚未合并到“扩展的预先生成的模式”中的质数以查找仍需要标记为“非质数”的数字,您需要为1000000000到11000000000的数字生成表格。

答案 6 :(得分:0)

Toomtarm Kung'的回答很好,非常感谢。

但是我仍然偶然发现了一个警告:(i*i)可能在32位的i>46340和64位的i>3037000499上溢出,产生错误的结果,即2147483647将使用32位整数时不能被识别为素数。

为避免整数溢出,可以将(i * i)<=number替换为i <= number / i

现在,为避免重复除法,代码可以编写如下:

int isPrime(int number){
    if(number < 2) return 0;
    if(number == 2) return 1;
    if(number % 2 == 0) return 0;
    int j;
    for(int i=3; ((j=number/i) >= i); i+=2){
        if(number - (j * i) == 0 ) return 0;
    }
    return 1;
}
相关问题