C - 确定数字是否为素数

时间:2009-10-08 15:45:12

标签: c# c primes

我试图想出一个方法,它接受一个整数并返回一个布尔值来说明数字是否为素数而且我不太了解C;有人会关心给我一些指示吗?

基本上,我会在C#中这样做:

static bool IsPrime(int number)
{
    for (int i = 2; i < number; i++)
    {
        if (number % i == 0 && i != number)
            return false;
    }
    return true;
}

10 个答案:

答案 0 :(得分:148)

好的,所以忘了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中没有bool类型,没有truefalse,所以你需要稍微修改一下(编辑: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(数字)时你可以停止检查。由于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;
}

这绝对不是检查一个数字是否为素数的最快方法,但它有效,而且非常简单。我们几乎不需要修改你的代码!

答案 1 :(得分:27)

我很惊讶没有人提到这一点。

使用Sieve Of Eratosthenes

详细信息:

  1. 除了1和他们自己
  2. 之外,基本上非主要数字可以被另一个数字整除
  3. 因此:非主要数字将是素数的乘积。
  4. Eratosthenes的筛子找到一个素数并存储它。当检查新数字的素数时,将根据已知的素数列表检查所有先前的素数。

    原因:

    1. 此算法/问题称为“Embarrassingly Parallel
    2. 它创建一个素数集合
    3. 它是动态编程问题的一个例子
    4. 很快!

答案 2 :(得分:15)

斯蒂芬·佳能很好地回答了这个问题!

但是

  • 通过观察所有素数的形式为6k±1,可以进一步改进算法,但2和3除外。
  • 这是因为对于某些整数k和i = -1,0,1,2,3或4,所有整数可以表示为(6k + i); 2除(6k + 0),(6k + 2),(6k + 4);和3分(6k + 3)。
  • 因此,更有效的方法是测试n是否可被2或3整除,然后检查所有形式的数字6k±1≤√n。
  • 这是测试所有m到√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; 
        }
    }
    

答案 3 :(得分:10)

  1. 构建一个小素数表,并检查它们是否除以输入数字。
  2. 如果数字存活到1,请尝试增加基础的伪素性测试。例如,请参阅Miller-Rabin primality test
  3. 如果你的数字存活到2,你可以得出结论,如果它低于一些众所周知的边界。否则你的答案只会是“可能是素数”。您将在Wiki页面中找到这些边界的一些值。

答案 4 :(得分:3)

检查每个整数的模数,从2到你正在检查的数字的根。

如果模数等于零,那么它不是素数。

伪代码:

bool IsPrime(int target)
{
  for (i = 2; i <= root(target); i++)
  {
    if ((target mod i) == 0)
    {
      return false;
    }
  }

  return true;
}

答案 5 :(得分:3)

在阅读完这个问题之后,我对一些答案感到好奇,因为一些答案通过运行一个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个不同质数的测试:

$ 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,因为增益不是那么大。但是,如果愿意编码,这个列表还可以。问题是你不能在没有循环的情况下陈述所有情况,或者你的代码会非常大(在相应的内部ORs中会有1658879 ||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

答案 6 :(得分:2)

我只想补充一点,偶数(第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;
}

答案 7 :(得分: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 =&gt; E,E * O =&gt; E,O * E =&gt; E和O * O =&gt; O)。

分区/模数在x86架构上确实代价高昂,尽管成本有多大(见http://gmplib.org/~tege/x86-timing.pdf)。另一方面,乘法非常便宜。

答案 8 :(得分:0)

使用Sieve of Eratosthenes,与“已知范围”的素数算法相比,计算速度更快。

通过使用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。

注意 :值可能因硬件规格而异。

答案 9 :(得分:0)

使用for (i = 2; i <= number/i; i++)限制迭代次数。

number%inumber/i在许多编译器上都是有效的,因为商和余数的计算是一起完成的,因此与之相对,只需花费1,就不会产生额外的成本。

一个非常简单的解决方案:

bool IsPrime(unsigned number) {
    for(unsigned i = 2; i <= number/i; i++){
        if(number % i == 0){
            return false;
        }
    }
    return number >= 2;
}