【问题标题】:C-greatest prime factor of a given numberC-给定数的最大素数
【发布时间】:2013-10-05 07:14:41
【问题描述】:

我编写了以下代码来计算所提供数字的最大素因子 通过标准输入,但执行需要很长时间。如何减少程序的执行时间?

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

bool is_prime(unsigned long long num)
{
    if (num == 0 || num == 1)
        return false;
    if (num == 2)
        return true;
    for (unsigned long long j = 2; j < num; j++)
        if(num%j==0)
            return false;

    return true;
}

int main(void)
{
    unsigned long long n,i;
    printf("enter the number: ");
    scanf("%llu",&n);
    for(i=n-1;i>1;i--)
        if((is_prime(i)) && n%i==0)
            break;
    printf("%llu\n",i);

    return 0;
}

输入中的数字是 600851475143。

【问题讨论】:

  • is-prime中,除了检查2之外,你只需要检查直到num的平方根的奇数。有关一些有用信息,请参阅this answer
  • 如果一个数比平方根大一个因数,它必须也有一个比平方根小于的因数。因此,您只需要检查平方根即可找到这些因素中的较低者。
  • for(unsigned long long j = 2; 效率低下,因为您之前已经检查了 2。请改用for(unsigned long long j = 3;
  • 哦不,O(1) 额外的工作。
  • 找到数字时将它们分开。这使得搜索量很小。此外,直到最后你才真正关心因子是否为素数,所以不要测试素数。这比分工贵。如果你先测试较小的数字,那么你已经知道你找到的第一个因素是素数。 (如果不是,您会首先找到其中一个因数。)前一百万个素数的硬编码表也会有所帮助。

标签: c execution-time


【解决方案1】:
bool is_prime(unsigned long long num)
{
    if (num <  2) return false;      /* Reject border cases */

    if (num == 2) return true;       /* Accept the largest even prime */

    if ((num & (~1)) == num) return false;  /* Reject all other evens */

    unsigned long long limit = sqrt(num) + 1;  /* Limit the range of the search */

    for (unsigned long long j = 3; j < limit; j += 2)  /* Loop through odds only */
        if(num % j==0)
            return false;           /* Reject factors */

    return true;                    /* No factors, hence it is prime */

}

【讨论】:

  • 我不在要测试的编译器附近。 sqrt(num) 变成 long long 是一种值得怀疑的方法。
  • j &lt; limit 应该是 j &lt;= limit。例如,如果我选择 num=9 会怎样?循环甚至不会运行。与 num=25,49,81,... 相同。
  • @chrono 我在限制中添加了一个。我的强迫症不喜欢
  • stackoverflow.com/questions/18499492/… 与 64 位整数一样重要,确实应该有一个函数来完成这项工作,而无需与双精度数进行转换
  • 确实应该有,但做起来并不难。代码也在那里。例如,this one
【解决方案2】:

玩得开心...一个函数用于所有素因数,一个用于最大素因数:

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

typedef unsigned long long u64_t;

static size_t prime_factors(u64_t value, u64_t *array)
{
    size_t n_factors = 0;
    u64_t n = value;
    u64_t i;

    while (n % 2 == 0)
    {
        n /= 2;
        //printf("Factor: %llu (n = %llu)\n", 2ULL, n);
        array[n_factors++] = 2;
    }

    while (n % 3 == 0)
    {
        n /= 3;
        //printf("Factor: %llu (n = %llu)\n", 3ULL, n);
        array[n_factors++] = 3;
    }

    for (i = 6; (i - 1) * (i - 1) <= n; i += 6)
    {   
        while (n % (i-1) == 0)
        {
            n /= (i-1);
            //printf("Factor: %llu (n = %llu)\n", (i-1), n);
            array[n_factors++] = (i-1);
        }
        while (n % (i+1) == 0)
        {
            n /= (i+1);
            //printf("Factor: %llu (n = %llu)\n", (i+1), n);
            array[n_factors++] = (i+1);
        }
    }
    if (n != 1)
        array[n_factors++] = n;

    return n_factors;
}

static u64_t max_prime_factor(u64_t value)
{
    u64_t n = value;
    u64_t i;
    u64_t max = n;

    while (n % 2 == 0)
    {
        n /= 2;
        //printf("Factor: %llu (n = %llu)\n", 2ULL, n);
        max = 2;
    }

    while (n % 3 == 0)
    {
        n /= 3;
        //printf("Factor: %llu (n = %llu)\n", 3ULL, n);
        max = 3;
    }

    for (i = 6; (i - 1) * (i - 1) <= n; i += 6)
    {   
        while (n % (i-1) == 0)
        {
            n /= (i-1);
            //printf("Factor: %llu (n = %llu)\n", (i-1), n);
            max = (i-1);
        }
        while (n % (i+1) == 0)
        {
            n /= (i+1);
            //printf("Factor: %llu (n = %llu)\n", (i+1), n);
            max = (i+1);
        }
    }
    //printf("Max Factor = %llu\n", (n == 1) ? max : n);

    return (n == 1) ? max : n;
}

static void print_factors(u64_t value, size_t n_factors, u64_t *factors)
{
    printf("%20llu:", value);
    int len = 21;
    for (size_t i = 0; i < n_factors; i++)
    {
        if (len == 0)
            len = printf("%.21s", "");
        len += printf(" %llu", factors[i]);
        if (len >= 60)
        {
            putchar('\n');
            len = 0;
        }
    }
    if (len > 0)
        putchar('\n');
}

void builtin_tests(void)
{
    u64_t tests[] =
    {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
        19, 20, 25, 49, 63, 69, 71, 73, 825, 829, 1000, 6857, 73112,
        731122, 7311229, 73112293, 731122935, 7311229357, 73112293571,
        600851475143, 731122935711, 7311229357111,
        18446744073709551610ULL, 18446744073709551611ULL,
        18446744073709551612ULL, 18446744073709551613ULL,
        18446744073709551614ULL, 18446744073709551615ULL,
    };
    u64_t factors[64];

    for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
    {
        u64_t max = max_prime_factor(tests[i]);
        printf("%20llu -> Max Prime Factor = %llu\n", tests[i], max);
        size_t n_factors = prime_factors(tests[i], factors);
        print_factors(tests[i], n_factors, factors);
    }
}

int main(int argc, char **argv)
{
    if (argc == 1)
        builtin_tests();
    else
    {
        for (int i = 1; i < argc; i++)
        {
            u64_t factors[64];
            u64_t value = strtoull(argv[i], 0, 0);
            u64_t max = max_prime_factor(value);
            printf("%20llu -> Max Prime Factor = %llu\n", value, max);
            size_t n_factors = prime_factors(value, factors);
            print_factors(value, n_factors, factors);
        }
    }
    return 0;
}

结果

                   1 -> Max Prime Factor = 1
                   1:
                   2 -> Max Prime Factor = 2
                   2: 2
                   3 -> Max Prime Factor = 3
                   3: 3
                   4 -> Max Prime Factor = 2
                   4: 2 2
                   5 -> Max Prime Factor = 5
                   5: 5
                   6 -> Max Prime Factor = 3
                   6: 2 3
                   7 -> Max Prime Factor = 7
                   7: 7
                   8 -> Max Prime Factor = 2
                   8: 2 2 2
                   9 -> Max Prime Factor = 3
                   9: 3 3
                  10 -> Max Prime Factor = 5
                  10: 2 5
                  11 -> Max Prime Factor = 11
                  11: 11
                  12 -> Max Prime Factor = 3
                  12: 2 2 3
                  13 -> Max Prime Factor = 13
                  13: 13
                  14 -> Max Prime Factor = 7
                  14: 2 7
                  15 -> Max Prime Factor = 5
                  15: 3 5
                  16 -> Max Prime Factor = 2
                  16: 2 2 2 2
                  17 -> Max Prime Factor = 17
                  17: 17
                  18 -> Max Prime Factor = 3
                  18: 2 3 3
                  19 -> Max Prime Factor = 19
                  19: 19
                  20 -> Max Prime Factor = 5
                  20: 2 2 5
                  25 -> Max Prime Factor = 5
                  25: 5 5
                  49 -> Max Prime Factor = 7
                  49: 7 7
                  63 -> Max Prime Factor = 7
                  63: 3 3 7
                  69 -> Max Prime Factor = 23
                  69: 3 23
                  71 -> Max Prime Factor = 71
                  71: 71
                  73 -> Max Prime Factor = 73
                  73: 73
                 825 -> Max Prime Factor = 11
                 825: 3 5 5 11
                 829 -> Max Prime Factor = 829
                 829: 829
                1000 -> Max Prime Factor = 5
                1000: 2 2 2 5 5 5
                6857 -> Max Prime Factor = 6857
                6857: 6857
               73112 -> Max Prime Factor = 37
               73112: 2 2 2 13 19 37
              731122 -> Max Prime Factor = 52223
              731122: 2 7 52223
             7311229 -> Max Prime Factor = 24953
             7311229: 293 24953
            73112293 -> Max Prime Factor = 880871
            73112293: 83 880871
           731122935 -> Max Prime Factor = 89107
           731122935: 3 5 547 89107
          7311229357 -> Max Prime Factor = 7311229357
          7311229357: 7311229357
         73112293571 -> Max Prime Factor = 8058227
         73112293571: 43 211 8058227
        600851475143 -> Max Prime Factor = 6857
        600851475143: 71 839 1471 6857
        731122935711 -> Max Prime Factor = 4973625413
        731122935711: 3 7 7 4973625413
       7311229357111 -> Max Prime Factor = 12939061
       7311229357111: 547 1033 12939061
18446744073709551610 -> Max Prime Factor = 1504703107
18446744073709551610: 2 5 23 53301701 1504703107
18446744073709551611 -> Max Prime Factor = 287630261
18446744073709551611: 11 59 98818999 287630261
18446744073709551612 -> Max Prime Factor = 2147483647
18446744073709551612: 2 2 3 715827883 2147483647
18446744073709551613 -> Max Prime Factor = 364870227143809
18446744073709551613: 13 3889 364870227143809
18446744073709551614 -> Max Prime Factor = 649657
18446744073709551614: 2 7 7 73 127 337 92737 649657
18446744073709551615 -> Max Prime Factor = 6700417
18446744073709551615: 3 5 17 257 641 65537 6700417

【讨论】:

    【解决方案3】:

    main() 中循环中的 if 语句应首先检查 n%i==0,以便不检查非因子的素数。在我在现代 Intel CPU 上运行的一个简单测试用例中,仅这一点就使程序在从 1 到 2000 的所有数字上的速度提高了 200 倍以上。下面的 mod 只是 main() (我把它分解成一个返回最大素数的函数)显着提高了速度。我也认为数字本身是一个可能的因素(原件中没有这样做)。

    unsigned long long largest_prime_factor(unsigned long long n)
    
        {
        unsigned long long i,sqrtn,maxprime;
    
        if (is_prime(n))
            return(n);
        sqrtn=(unsigned long long)sqrt((double)n);
        maxprime=1;
        for (i=2;i<=sqrtn;i++)
            {
            if (n%i==0)
                {
                unsigned long long f;
    
                f=n/i;
                if (is_prime(f))
                    return(f);
                if (is_prime(i))
                    maxprime=i;
                }
            }
        return(maxprime);
        }
    

    【讨论】:

      【解决方案4】:

      保持相同的逻辑,您可以通过仅执行直到 sqrt(num) 的循环来减少时间。 因为如果它不是素数,至少会有一个除数小于 sqrt(num)。

      让我们假设 num 不是素数。所以对于一些 1

      所以 a 或 b 应该小于 sqrt(num)。

      因此,您的 is_prime 代码应更改为如下代码

      bool is_prime(unsigned long long num)
      {
          if (num == 0 || num == 1)
              return false;
          if (num == 2)
              return true;
          long long root_num = sqrt(num);
          for (unsigned long long j = 2; j < root_num; j++)
              if(num%j==0)
                  return false;
      
          return true;
      }
      

      现在如果你想改变你的逻辑,那么有一种称为筛法的算法可以找到所有小于给定数的素数。看看这个Sieve Algorithm

      【讨论】:

      • 当然for (unsigned long long j = 3; j &lt; root_num; j+=2) 会更好? (2 已经过测试,剩余的主要因素必须是奇数)。
      • 嘿,罗杰,我说过在 is_prime 函数中保持相同的逻辑会更好。在 is_prime() 我们没有找到素数。我们正在寻找一个数字的除数,对吧?跳过偶数来测试 is_prime 是另一回事。
      • 如果您正在寻找一个数的除数,并且您已经发现 2 不是其中之一,那么检查任何其他偶数是没有意义的,因为如果一个偶数是一个除数,那么 2 也将是一个除数。因此,您可以通过仅检查奇数除数将循环的大小减半。
      • 先生,您说得对。但是我要说的是,即使您执行 j+=2 也不会对复杂性产生任何影响。这将是 O(num/2)。即仍然 O(num),与原始代码相同。当然它会有点效率,但不是复杂性。这就是为什么我没有让他做 j += 2;谢谢。
      • 好的,但他在问如何减少执行时间,这就是我的评论相关的原因。这是一个小问题,其他人在他们的回答中已经考虑到了,所以我只是想你也想改进你的,这没什么大不了的。
      【解决方案5】:

      最好从数字的平方根到 3 开始循环, 再次,您可以跳过偶数。

      这样你就可以在短时间内得到答案..

      【讨论】:

        猜你喜欢
        • 2021-05-12
        • 1970-01-01
        • 2017-01-29
        • 2011-05-06
        • 2013-09-09
        • 1970-01-01
        • 1970-01-01
        • 2015-11-18
        • 1970-01-01
        相关资源
        最近更新 更多