有谁能告诉我如何在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是要打印质数的范围。
答案 0 :(得分:9)
您需要创建一个与您要查找的最大素数一样大的布尔数组。在开始时它完全初始化为真。
如果i
是素数,则此类数组的i
单元格将为true;如果不是,则为false。
从i=2
开始迭代:它是素数,然后将索引倍数为2的任何单元格设置为false。转到下一个素数(i=3
)并执行相同操作。转到下一个素数(它是i=5
:i=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;
}