我正试图在C中设计一个eratosthenes的筛子,但我遇到了两个我无法弄清楚的奇怪问题。这是我的基本课程大纲。要求用户设置范围以显示素数。如果范围最小值低于9,则将最小值设置为9.使用范围内的所有奇数填充数组。
1)我试图通过声明可变大小的数组来减少内存使用量:
if (max<=UINT_MAX)
unsigned int range[(max-min)/2];
else if (max<=ULONG_MAX)
unsigned long int range[(max-min)/2];
else if (max<=ULLONG_MAX)
unsigned long long int range[(max-min)/2];
为什么不编译?变量min和max在前面声明为int,包含limits.h。我已经注释掉了选择结构,现在只声明了unsigned long long int range[(max-min)/2];
,它现在编译并运行。
2)我的代码运行但它有时会将小素数标记为非素数。
#include<stdio.h>
#include<limits.h>
void prime(int min, int max)
{
int i, f=0;
//declare variable size array
/*if (max<=(int)UINT_MAX)
unsigned int range[(max-min)/2];
else if (max<=(int)ULONG_MAX)
unsigned long int range[(max-min)/2];
else if (max<=(int)ULLONG_MAX)*/
unsigned long long int range[(max-min)/2];
//fill array with all odd numbers
if (min%2==0)
{
for (i=min+1;i<=max;i+=2)
{
range[f]=i;
f+=1;
}
}
else
{
for (i=min;i<=max;i+=2)
{
range[f]=i;
f+=1;
}
}
//assign 0 to cell if divisible by any number other than itself
for (i=3;i<=sqrt(max);++i)
{
for (f=0;f<=((max-min)/2);f++)
{
if (range[f]%i==0 && f!=i)
range[f]=0;
}
}
//troubleshoot only: print full range
for (f=0;f<=((max-min)/2);f++)
{
printf("ALL: %d / %d\n", f, range[f]);
}
//display all primes
if (min==9) /*print primes lower than 9 for ranges where min<9*/
printf("2\n3\n5\n7\n");
for (f=0;f<=((max-min)/2);f++) /*print non 0 numbers in array*/
{
if (range[f]!=0)
printf("%d\n", range[f]);
}
}
int main(void)
{
int digits1, digits2;
printf("\n\n\nCalculate Prime Numbers\n");
printf("This program will display all prime numbers in a given range. \nPlease set the range.\n");
printf("Minimum: ");
scanf("%d", &digits1);
if (digits1<9)
digits1=9;
printf("Maximum: ");
scanf("%d", &digits2);
printf("Calculating...");
printf("All prime numbers between %d and %d are:\n", digits1, digits2);
prime(digits1, digits2);
getchar();
getchar();
}
例如,如果数字= 1且数字2 = 200,我的程序会输出1到200之间的所有素数,除了11和13. 11和13被筛选掉了,我无法弄清楚为什么这会发生越来越多的低数字随着digits2的增加。
3)最后,我的筛子是否适当筛选了eratosthenes?它有点工作,但我觉得有一种更有效的筛选非素数的方法,但我无法弄清楚如何实现它。我的这个计划的目标之一是尽可能高效。同样,我现在所拥有的是:
//assign 0 to cell if divisible by any number other than itself
for (i=3;i<=sqrt(max);++i)
{
for (f=0;f<=((max-min)/2);f++)
{
if (range[f]%i==0 && f!=i)
range[f]=0;
}
}
感谢您阅读所有内容!我很抱歉发布了另一个与eratosthenes相关问题的筛子,并提前感谢您的帮助!
答案 0 :(得分:3)
不,这不是Eratosthenes的合适筛子。在Eratosthenes算法的筛子中没有涉及剩余物的测试,Wikipedia is real clear on this我认为。 :)重点是避免试验分裂,免费获得素数,而不进行测试。
如何?通过生成它们的倍数,从我们识别的每个素数,一个接一个的升序。
素数 p 的倍数为: 2p,2p + p,2p + p + p,...
素数 p 的奇数倍是: 3p,3p + 2p,3p + 2p + 2p,...
当我们枚举它们时,我们在筛子阵列中标记它们。有些会被标记两次或更多,例如15将被标记为3和5(因为 3 * 5 == 5 * 3 )。因此,我们可以从 p 2 开始枚举和标记:
for( i=3; i*i < n; i += 2 )
if( !sieve[i] ) // if `i` is not marked as composite
for( j = i*i; j < n; j += 2*i )
{
sieve[j] = 1; // 1 for composite, initially all are 0s
}
筛子的关键是:我们不将数字存储在数组中。它不是INT
的数组;它是一个1位标志的数组,值为0或1。筛子阵列中条目的索引表示筛子保持其状态的数量:标记的,即复合的,或尚未标记的,即潜在的质数。
所以最后,所有未标记的条目都表示素数。您当然需要设计一种寻址方案,例如:索引i
的条目可能对应于a + 2*i
的数字,其中a
是范围的奇数开头。由于您的范围从某个偏移量开始,因此该方案称为offset sieve of Eratosthenes。骨架C实现是here。
为了最大限度地减少内存使用,我们需要将数组视为位数组。在C ++中,例如这很简单:我们将其声明为vector<bool>
,它会自动为我们打包。在C中,我们必须自己做一些打包和打包。
建议:不要对临时变量吝啬。为程序中的每个有意义的实体命名。您的代码中不应该有(max-min)/2
;而是定义width = max - min
并使用该名称。将优化保留在编译器中。 :)
关于你的第一个问题:这是范围的事情。您的代码等同于
if (max<=UINT_MAX)
{ unsigned int range[(max-min)/2]; } // note the curly braces!
else if (max<=ULONG_MAX)
{ unsigned long int range[(max-min)/2]; }
else if (max<=ULLONG_MAX)
{ unsigned long long int range[(max-min)/2]; }
所以这里有三个range
数组声明,每个声明都在相应的块内。每个都是在进入其封闭块({
)时创建的,并在退出时被销毁(}
)。换句话说,它不再存在于prime
函数的其余部分。实际上,这意味着如果您在if
块中声明变量,则只能在该块内使用它(在相应的大括号{
和}
之间)。
答案 1 :(得分:0)
Q1:您不能在同一范围内两次声明符号(此处为range
)。这不是您的问题,但您尝试执行此操作:在range
范围内声明if
,但在外部不可见。