我试图提出一个方法,该方法接受一个整数并返回一个布尔值,以表明数字是否为质数,并且我对C的了解不多。有人愿意给我一些指示吗?
基本上,我会在C#中这样做:
static bool IsPrime(int number)
{
for (int i = 2; i < number; i++)
{
if (number % i == 0 && i != number)
return false;
}
return true;
}
我试图提出一个方法,该方法接受一个整数并返回一个布尔值,以表明数字是否为质数,并且我对C的了解不多。有人愿意给我一些指示吗?
基本上,我会在C#中这样做:
static bool IsPrime(int number)
{
for (int i = 2; i < number; i++)
{
if (number % i == 0 && i != number)
return false;
}
return true;
}
i < number是过大的。根据定义,如果一个数字x = a * b,要么a或者b是< int(sqrt(x))另一种是更大的。因此,您的循环只需要上升到即可int(sqrt(x))。
Answers:
好,所以就不用C了。假设我给您一个数字,请您确定它是否是素数。你怎么做呢?清楚地写下步骤,然后担心将它们转换为代码。
确定算法后,您将更容易弄清楚如何编写程序,而其他人则可以帮助您。
编辑:这是您发布的C#代码:
static bool IsPrime(int number) {
for (int i = 2; i < number; i++) {
if (number % i == 0 && i != number) return false;
}
return true;
}
照原样,这几乎是有效的C。boolC中没有类型,notrue或no false,因此您需要对其进行一些修改(编辑:Kristopher Johnson正确指出C99添加了stdbool.h标头)。由于有些人无权访问C99环境(但您应该使用一个!),所以让我们进行一次非常小的更改:
int IsPrime(int number) {
int i;
for (i=2; i<number; i++) {
if (number % i == 0 && i != number) return 0;
}
return 1;
}
这是一个完全有效的C程序,可以满足您的需求。我们可以毫不费力地改善它。首先,请注意i始终小于number,因此i != number始终成功的检查;我们可以摆脱它。
同样,您实际上并不需要一直尝试除数number - 1;您可以在到达sqrt(number)时停止检查。由于这sqrt是浮点运算,并且会带来很多细微的差别,因此我们实际上不会进行计算sqrt(number)。相反,我们可以检查一下i*i <= number:
int IsPrime(int number) {
int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
最后一件事;您的原始算法中有一个小错误!如果number为负数,零或一,则此函数将声称该数字为质数。您可能希望适当地处理它,并且可能希望使其number不带符号,因为您更可能只关心正值:
int IsPrime(unsigned int number) {
if (number <= 1) return 0; // zero and one are not prime
unsigned int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
这绝对不是检查数字是否为质数的最快方法,但它确实有效,而且非常简单。我们几乎不需要修改您的代码!
bool,true和false。
我很惊讶没有人提到这一点。
细节:
Eratosthenes的筛子找到质数并将其存储。当检查新数字的质数时,将根据已知质数列表检查所有先前的质数。
原因:
O(n)空间中,并且只要您的计算是针对单个值的,这都是巨大的空间浪费,并且不会提高性能。
O(n log n)或更大,如果您支持大量…)
static const位号为主要数字的位图并使用它,而不是在运行时填充它。
斯蒂芬·佳能回答得很好!
但
这是测试所有直到√n的速度的3倍。
int IsPrime(unsigned int number) {
if (number <= 3 && number > 1)
return 1; // as 2 and 3 are prime
else if (number%2==0 || number%3==0)
return 0; // check if number is divisible by 2 or 3
else {
unsigned int i;
for (i=5; i*i<=number; i+=6) {
if (number % i == 0 || number%(i + 2) == 0)
return 0;
}
return 1;
}
}
0在(number == 1)时返回,因为1不是质数。
此程序对于检查单个数字进行素数检查非常有效。
bool check(int n){
if (n <= 3) {
return n > 1;
}
if (n % 2 == 0 || n % 3 == 0) {
return false;
}
int sq=sqrt(n); //include math.h or use i*i<n in for loop
for (int i = 5; i<=sq; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
i=2到一直进行i<=ceil(sqrt(n))。您在您的测试错过了2号:首先,投给(int)品牌sqrt(n)主干小数。其次,您使用i<sq了应该使用的时间i<=sq。现在,假设有一个适合这个问题的数字。n具有ceil(sqrt(n))较小因子的复合数字。您的内部循环会像(5、7),(11、13),(17、19),(23、25),(29、31),(35、37),(41、43),依此类推,n%i等等n%(i+2)。假设我们得到了sqrt(1763)=41.98。作为1763=41*43一个复合数字。您的循环只会运行直到(35, 37)失败。
ceil()问题之后,我意识到尽管很多网站都推荐它,但是这只是过分的了。您可以中继并测试i<=sqrt(n),就可以了。测试用例是较大的补间质数。示例:86028221*86028223=7400854980481283和sqrt(7400854980481283)~86028222。而较小的已知补间质数2和3,使sqrt(6)=2.449树干仍会离开2。(但是较小的不是测试用例,只是为了说明一点的比较)。因此,是的,该算法现在是正确的。无需使用ceil()。
检查从2到要检查数字的根的每个整数的模数。
如果模量等于零,则不是质数。
伪代码:
bool IsPrime(int target)
{
for (i = 2; i <= root(target); i++)
{
if ((target mod i) == 0)
{
return false;
}
}
return true;
}
在阅读了这个问题之后,我对以下事实感到好奇:一些答案通过运行2 * 3 = 6的倍数的循环提供了优化。
因此,我以相同的想法创建了一个新函数,但其倍数为2 * 3 * 5 = 30。
int check235(unsigned long n)
{
unsigned long sq, i;
if(n<=3||n==5)
return n>1;
if(n%2==0 || n%3==0 || n%5==0)
return 0;
if(n<=30)
return checkprime(n); /* use another simplified function */
sq=ceil(sqrt(n));
for(i=7; i<=sq; i+=30)
if (n%i==0 || n%(i+4)==0 || n%(i+6)==0 || n%(i+10)==0 || n%(i+12)==0
|| n%(i+16)==0 || n%(i+22)==0 || n%(i+24)==0)
return 0;
return 1;
}
通过运行两个功能并检查时间,我可以说该功能确实更快。让我们看一下使用2个不同素数的2个测试:
$ time ./testprimebool.x 18446744069414584321 0
f(2,3)
Yes, its prime.
real 0m14.090s
user 0m14.096s
sys 0m0.000s
$ time ./testprimebool.x 18446744069414584321 1
f(2,3,5)
Yes, its prime.
real 0m9.961s
user 0m9.964s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 0
f(2,3)
Yes, its prime.
real 0m13.990s
user 0m13.996s
sys 0m0.004s
$ time ./testprimebool.x 18446744065119617029 1
f(2,3,5)
Yes, its prime.
real 0m10.077s
user 0m10.068s
sys 0m0.004s
所以我想,如果将其概括化,会有人获得太多收益吗?我想出了一个函数,该函数将首先进行围攻以清除给定的原始素数列表,然后使用该列表计算较大的素数。
int checkn(unsigned long n, unsigned long *p, unsigned long t)
{
unsigned long sq, i, j, qt=1, rt=0;
unsigned long *q, *r;
if(n<2)
return 0;
for(i=0; i<t; i++)
{
if(n%p[i]==0)
return 0;
qt*=p[i];
}
qt--;
if(n<=qt)
return checkprime(n); /* use another simplified function */
if((q=calloc(qt, sizeof(unsigned long)))==NULL)
{
perror("q=calloc()");
exit(1);
}
for(i=0; i<t; i++)
for(j=p[i]-2; j<qt; j+=p[i])
q[j]=1;
for(j=0; j<qt; j++)
if(q[j])
rt++;
rt=qt-rt;
if((r=malloc(sizeof(unsigned long)*rt))==NULL)
{
perror("r=malloc()");
exit(1);
}
i=0;
for(j=0; j<qt; j++)
if(!q[j])
r[i++]=j+1;
free(q);
sq=ceil(sqrt(n));
for(i=1; i<=sq; i+=qt+1)
{
if(i!=1 && n%i==0)
return 0;
for(j=0; j<rt; j++)
if(n%(i+r[j])==0)
return 0;
}
return 1;
}
我以为我没有优化代码,但这是公平的。现在,进行测试。因为有这么多的动态内存,所以我期望2 3 5列表比2 3 5硬编码要慢一些。但这没关系,正如您所看到的。之后,时间变得越来越小,最终的最佳清单是:
2 3 5 7 11 13 17 19
用了8.6秒。因此,如果有人创建利用这种技术的硬编码程序,我建议使用清单2 3和5,因为收益并不那么大。而且,如果愿意编码,则此列表还可以。问题是你不能说出所有的情况下没有环,或者你的代码将是非常大的(会有1658879 ORs,即||在相应的内部if)。下一个列表:
2 3 5 7 11 13 17 19 23
时间开始变大,只有13秒。这里是整个测试:
$ time ./testprimebool.x 18446744065119617029 2 3 5
f(2,3,5)
Yes, its prime.
real 0m12.668s
user 0m12.680s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7
f(2,3,5,7)
Yes, its prime.
real 0m10.889s
user 0m10.900s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11
f(2,3,5,7,11)
Yes, its prime.
real 0m10.021s
user 0m10.028s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13
f(2,3,5,7,11,13)
Yes, its prime.
real 0m9.351s
user 0m9.356s
sys 0m0.004s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17
f(2,3,5,7,11,13,17)
Yes, its prime.
real 0m8.802s
user 0m8.800s
sys 0m0.008s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19
f(2,3,5,7,11,13,17,19)
Yes, its prime.
real 0m8.614s
user 0m8.564s
sys 0m0.052s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23
f(2,3,5,7,11,13,17,19,23)
Yes, its prime.
real 0m13.013s
user 0m12.520s
sys 0m0.504s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 29
f(2,3,5,7,11,13,17,19,23,29)
q=calloc(): Cannot allocate memory
PS。我没有故意释放(r),将任务交给操作系统,因为一旦程序退出,内存将被释放,以节省一些时间。但是,如果您打算在计算后继续运行代码,则最好将其释放。
奖金
int check2357(unsigned long n)
{
unsigned long sq, i;
if(n<=3||n==5||n==7)
return n>1;
if(n%2==0 || n%3==0 || n%5==0 || n%7==0)
return 0;
if(n<=210)
return checkprime(n); /* use another simplified function */
sq=ceil(sqrt(n));
for(i=11; i<=sq; i+=210)
{
if(n%i==0 || n%(i+2)==0 || n%(i+6)==0 || n%(i+8)==0 || n%(i+12)==0 ||
n%(i+18)==0 || n%(i+20)==0 || n%(i+26)==0 || n%(i+30)==0 || n%(i+32)==0 ||
n%(i+36)==0 || n%(i+42)==0 || n%(i+48)==0 || n%(i+50)==0 || n%(i+56)==0 ||
n%(i+60)==0 || n%(i+62)==0 || n%(i+68)==0 || n%(i+72)==0 || n%(i+78)==0 ||
n%(i+86)==0 || n%(i+90)==0 || n%(i+92)==0 || n%(i+96)==0 || n%(i+98)==0 ||
n%(i+102)==0 || n%(i+110)==0 || n%(i+116)==0 || n%(i+120)==0 || n%(i+126)==0 ||
n%(i+128)==0 || n%(i+132)==0 || n%(i+138)==0 || n%(i+140)==0 || n%(i+146)==0 ||
n%(i+152)==0 || n%(i+156)==0 || n%(i+158)==0 || n%(i+162)==0 || n%(i+168)==0 ||
n%(i+170)==0 || n%(i+176)==0 || n%(i+180)==0 || n%(i+182)==0 || n%(i+186)==0 ||
n%(i+188)==0 || n%(i+198)==0)
return 0;
}
return 1;
}
时间:
$ time ./testprimebool.x 18446744065119617029 7
h(2,3,5,7)
Yes, its prime.
real 0m9.123s
user 0m9.132s
sys 0m0.000s
101-199原始在这里都失败了,因为101 % (11+90)。
n%(i+86)检查一下n > i+k
check235()素数
i+arr[k] >= n
if编译器可以更好地优化with常量。我进行了编辑,以添加异常并保持当前结构完整。但我同意,用阵列对人眼会更好。
我要补充一点,没有偶数(第2条)可以是质数。这导致在for循环之前出现另一种情况。因此,最终代码应如下所示:
int IsPrime(unsigned int number) {
if (number <= 1) return 0; // zero and one are not prime
if ((number > 2) && ((number % 2) == 0)) return 0; //no even number is prime number (bar 2)
unsigned int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
int is_prime(int val)
{
int div,square;
if (val==2) return TRUE; /* 2 is prime */
if ((val&1)==0) return FALSE; /* any other even number is not */
div=3;
square=9; /* 3*3 */
while (square<val)
{
if (val % div == 0) return FALSE; /* evenly divisible */
div+=2;
square=div*div;
}
if (square==val) return FALSE;
return TRUE;
}
2和偶数的处理将保留在主循环之外,该主循环仅处理除以奇数的奇数。这是因为对偶数取模的奇数将始终给出非零的答案,这使这些测试变得多余。或者换句话说,一个奇数可以被另一个奇数均匀除,但不能被偶数除(E * E => E,E * O => E,O * E => E和O * O => O)。
在x86架构上,除法/模数确实非常昂贵,尽管其代价会有所不同(请参阅http://gmplib.org/~tege/x86-timing.pdf)。另一方面,乘法非常便宜。
避免溢出错误
unsigned i, number;
...
for (i=2; i*i<=number; i++) { // Buggy
for (i=2; i*i<=number; i += 2) { // Buggy
// or
for (i=5; i*i<=number; i+=6) { // Buggy
当number为素数且i*i接近该类型的最大值时,这些形式不正确。
所有整数类型都存在问题,signed, unsigned并且范围更广。
例:
让UINT_MAX_SQRT作为最大整数的平方根的地板上。例如65535,当unsigned是32位时。
使用for (i=2; i*i<=number; i++),出现10年的故障是因为当UINT_MAX_SQRT*UINT_MAX_SQRT <= number和number是素数时,下一次迭代会导致乘法溢出。如果类型是带符号的类型,则溢出为UB。对于unsigned类型,它本身不是UB,但逻辑已损坏。继续进行交互,直到截断的产品超过为止number。结果可能不正确。带32位unsigned,请尝试4,294,967,291,这是一个素数。
如果some_integer_type_MAX曾经是Mersenne Prime,那i*i<=number将永远不是事实。
为避免此错误,请考虑number%i,number/i作为商和余数的计算完成在一起,从而招致没有额外的费用做到既与仅有1是许多编译器效率。
一个简单的全方位解决方案:
bool IsPrime(unsigned number) {
for(unsigned i = 2; i <= number/i; i++){
if(number % i == 0){
return false;
}
}
return number >= 2;
}
与“已知范围的”素数算法相比,使用Eratosthenes的Sieve可以更快地进行计算。
通过使用其Wiki(https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)中的伪代码,我可以在C#上获得解决方案。
public bool IsPrimeNumber(int val) {
// Using Sieve of Eratosthenes.
if (val < 2)
{
return false;
}
// Reserve place for val + 1 and set with true.
var mark = new bool[val + 1];
for(var i = 2; i <= val; i++)
{
mark[i] = true;
}
// Iterate from 2 ... sqrt(val).
for (var i = 2; i <= Math.Sqrt(val); i++)
{
if (mark[i])
{
// Cross out every i-th number in the places after i (all the multiples of i).
for (var j = (i * i); j <= val; j += i)
{
mark[j] = false;
}
}
}
return mark[val];
}
IsPrimeNumber(1000000000)需要21s 758ms。
注意:值可能会因硬件规格而异。