【问题标题】:Finding biggest prime number in user-inputted number - C在用户输入的数字中找到最大的素数 - C
【发布时间】:2019-02-15 00:45:16
【问题描述】:

我的代码有问题。题目是编写一个C程序,在用户输入的数字中找到最大的素数。

例如: 输入号码:46656665326

输出:66566653

这是我的代码:

#include <stdio.h>
#include <stdlib.h>

int is_prime(unsigned long long a)
{
    if(a<=1)
        return 0;
    if(a==2)
        return 1;
    for(unsigned long long p=2; p<a; p++)
        if(a%p==0)
            return 0;
    return 1;
}

unsigned long long find_largest_prime_number(unsigned long long number)
{
    unsigned long long prime=0;
    int count=0;
    unsigned long long count2=1;
    unsigned long long pom=0;
    unsigned long long pom3=0;
    pom3=number;
    while(pom3!=0)
    {
        count++;
        pom3/=10;
    }
    count++;
    int pom_1=0;
    while(pom_1<count)
    {
        count2*=10;
        pom_1++;
    }
    pom=number;
    while(count2>=10)
    {
        unsigned long long pom2=pom;
        while(pom2!=0)
        {
            if(is_prime(pom2))
                if(pom2>prime)
                    prime=pom2;
            pom2/=10;
        }
        count2/=10;
        pom=pom%count2;
    }
    return prime;
}

int main()
{
    unsigned long long x=0;
    printf("Enter number: ");
    int n1=scanf("%llu", &x);
    if(n1!=1)
    {
        printf("incorrect input");
        return 1;
    }
    printf("%llu", find_largest_prime_number(x));
    return 0;
}

问题是它适用于最大 13 位数字,但当输入数字超过 13 位时它会冻结。 前任。当我进入时它会冻结:215911504934497

请帮忙,代码有什么问题?

【问题讨论】:

  • @Blaze 不是 %llu 正确的 unsigned long long 吗? stackoverflow.com/questions/2844/…
  • 您尝试过什么调试问题?执行到底卡在哪里了?
  • countpom 是否有问题,因为它们是整数,可能不足以支持超过 13 位的 x
  • @NicoHaase 它适用于最大 13 位数字,但当输入数字超过 13 位时它会冻结。前任。当我输入时它会冻结:215911504934497,例如:imgur.com/271Ypy2(12 位数字,有效),imgur.com/EfoL2LQ(15 位数字,冻结)
  • @geekon 是的。代码完全正确。它的效率非常低,因此需要非常非常长的时间才能找出一个大数是否是素数。

标签: c


【解决方案1】:

阻塞的原因归结为:

int is_prime(unsigned long long a)
{
    ...
    for(unsigned long long p=2; p<a; p++)
        if(a%p==0)
            return 0;
    return 1;
}

如果您输入215911504934497,那么find_largest_prime_number 将调用is_prime(215911504934497)215911504934497 是一个很大的数字,对于从 2 到 215911504934497 的每个 p 执行 a%p 是 cpu 昂贵的(我认为至少你可以 p &lt; a/2)。你的程序陷入了这个循环。您可以通过在其中执行一个简单的 printf 来观察:

int is_prime(unsigned long long a)
{
    ...
    for(unsigned long long p=2; p<a; p++) {
        printf("%lld %lld\n", p, a);
        if(a%p==0)
            return 0;
    }
    return 1;
}

【讨论】:

    【解决方案2】:

    您的代码完全正确。它的效率非常低,因此需要非常非常长的时间才能找出单个大数是否为素数。

    这是is_prime的更好版本:

    • 它只测试除数,直到要测试的数字的平方根。
    • 它只测试奇数除数,如果这个数不能被2整除,那么测试它是否能被4、6、8等整除是没有意义的。

    // long long integer square root found somewhere on the internet
    unsigned long long isqrt(unsigned long long x)
    {
      unsigned long long op, res, one;
    
      op = x;
      res = 0;
    
      /* "one" starts at the highest power of four <= than the argument. */
      one = 1LL << 62;  /* second-to-top bit set */
      while (one > op) one >>= 2;
    
      while (one != 0) {
        if (op >= res + one) {
          op -= res + one;
          res += one << 1;  // <-- faster than 2 * one  
        }
        res >>= 1;
        one >>= 2;
      }
      return res;
    }
    
    
    int is_prime(unsigned long long a)
    {
      if (a <= 1 || a == 2 || a % 2 == 0)
        return 0;
    
      unsigned long long count = 0;
      unsigned long long limit = isqrt(a) + 1;
    
      for (unsigned long long p = 3; p < limit; p += 2)
      {
        if (a % p == 0)
          return 0;
      }
      return 1;
    }
    

    当然可以进行进一步优化。例如。如果数字不能被 3 整除,那么测试 3 的倍数也是毫无意义的。此外,如果您想找到一系列素数,可能还需要考虑其他方法。

    【讨论】:

    • 对于您所说的优化,您必须使用我的回答中详述的 Erathoneses 筛子
    • @Samleo 不确定,Erastothenes 的筛子非常适合小值,但对于大值,您只需要太多内存。但肯定有更高级的算法,我们在这里使用的算法非常幼稚且效率低下(即使经过我的改进)。
    【解决方案3】:

    关注平方根终于解决了这个问题。 is_prime 应该是这样的:

    int is_prime(unsigned long long a)
    {
        int i=0;
        int count=0;
        int test=0;
        int limit=sqrt(a)+1;
        if(a<=1)
            return 0;
        if(a==2)
            return 1;
        if(a%2==0)
            test=1;
        else
            for(i=3; i<limit && !test; i+=2, count++)
                if(a%i==0)
                    test=1;
        if(!test)
            return 1;
        else
            return 0;
    }
    

    【讨论】:

    • 我不确定sqrt 是否适用于巨大的 64 位整数。
    • @Jabberwocky 嗯,但它适用于超过 13 位数字的数字
    • 是的,但我不确定它是否适用于 17-18 位数字。不过我没有调查。
    • 好的,对你有好处。顺便说一句,你的图片不是证据,你应该看看ideone.com,这非常适合发布像this one for example这样的小程序
    • @Jabberwocky 谢谢,不知道这个网站
    【解决方案4】:

    正如其他贡献者所提到的,在 cmets 中,您的代码“崩溃”仅仅是因为它效率低下。

    许多其他贡献者使用了一种更有效的方法来检查一个数字是否为素数,方法是检查该数字与它的除数。

    但是,这不是最有效的方法,尤其是当您确定多个数字是否为素数时。

    为了让它更快,我建议实现the Sieve of Eratosthenes

    #define MAX_N 4294967296 //idk how big of an array your computer can actually handle. I'm using 2^32 here.
    
    //Declare as a global variable for extra memory allocation
    //unsigned char is used as it is only 1 byte (smallest possible memory alloc)
    //0 for FALSE, 1 for TRUE.
    unsigned char is_prime[MAX_N+1];
    
    //Populate the is_prime function up to your input number (or MAX_N, whichever is smaller)
    //This is done in O(N) time, where N is your number.
    void performSieve(unsigned long long number){
        unsigned long long i,j;
        unsigned long long n = (number>MAX_N)?MAX_N:number; //quick way (ternary operator): "whichever is smaller"
    
        //Populating array with default as prime
        for(i=2; i<=n; i++) is_prime[i] = 1;
    
        for(i=4; i<=n; i+=2) is_prime[i] = 0; //all even numbers except 4 is not prime
        for(i=3; i<=n; i+=2){
            if(is_prime[i] == 1)
                for(j=i*i;j<=n;j+=i){ //all the multiples of i except i itself are NOT prime
                    is_prime[i] == 0;
                }
        }    
    }
    
    //isPrime function
    unsigned char isPrime(unsigned long long n){
        if(n<=1) return 0; //edge cases
    
        //Check if we can find the prime number in our gigantic sieve
        if(n<=MAX_N){
            return is_prime[n]; //this is O(1) time (constant time, VERY FAST!)
        }
    
        //Otherwise, we now use the standard "check all the divisors" method
        //with all the optimisations as suggested by previous users:
        if(n%2==0) return 0; //even number
    
        //This is from user @Jabberwocky
        unsigned long long limit = isqrt(a);
    
        for (unsigned long long p = 3; p <= limit; p += 2) {
            if (a % p == 0) return 0;
        }
    
        return 1;
    }
    

    【讨论】:

    • 对于非常大的数字,您将无法使用该方法。在这里,您分配了 4GB 内存,不确定这是否适用于大多数平台,4294967296 在涉及大质数时并不是一个大数字。
    • 是的,我知道你会遇到麻烦,这就是为什么我添加了二次优化,如果数量太大,Erath 的筛子将无法工作。
    • 可能有一种方法可以使用更复杂的字符中的各个位来解决内存问题。因此,您将整数视为不是十进制整数,而是二进制数,其中各个位表示该数字是否为素数的真假
    猜你喜欢
    • 2015-07-16
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2016-03-08
    • 2016-06-13
    • 2019-10-16
    相关资源
    最近更新 更多