破解项目欧拉问题3,我的解决方案正确吗?

时间:2019-06-19 17:20:12

标签: c algorithm optimization

欧拉计划的问题3内容如下:

  

13195的主要因子是5、7、13和29。   600851475143中最大的素数是多少?

我已经提出了这个解决方案,很有意义,看起来还可以,适用于少量数字,但是当我们遇到问题时,最大的问题是程序永远运行。我的问题是,这从根本上是正确的,如果可以的话,我该如何优化代码?以我的理解,有问题的函数是is_prime()。

bool is_factor(long long int num, long long int factor)
{
    if(!(num%factor))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool is_prime(long long int num)
{
    long long int i;
    bool flag = true;
    for(i = 2; i <= num/2; i++)
    {
        if(!(num%i))
        {
            flag = false;
        }
    }
    return flag;
}

int main(void)
{
    long long int i, max_factor = 1;
    for(i = 1; i < 600851475143; i++)
    {
        if(is_factor(600851475143,i) && is_prime(i) && i>max_factor)
        {
            max_factor = i;
        }
    }
    printf("%d\n",max_factor);
    return 0;
}

1 个答案:

答案 0 :(得分:3)

到目前为止,您使用的一般策略总体上如下:

  1. 尝试将目标数字除以所有小于或等于目标数字一半的数字。
  2. 每次找到除数时,请查看它是否是质数且大于最大因数。如果是这样,请更新最大因子。
  3. 返回以这种方式找到的最大数字。

考虑到您的目标是找到单个数字的最大因子,所以这是一个相当合理的策略。有几种方法可以加快此速度。其中有些在评论中回显,而另一些(据我所知)尚未在其中提出。

优化1:消除原始性检查

现在,您将目标数除以每个可能的除数,然后检查这些除数是否为质数。如果您的目标数量有很多除数,那么您将花费大量时间检查不是质数的除数,这会占用您的运行时。

下面是一种替代方法。与以前一样,尝试按顺序将目标数除以每个可能的除数。但是,请做一个更改:只要找到除数,就可以通过划分尽可能多的除数来修改目标数。如果这样做,将会发生一些有趣的事情:将发现除以该数字的唯一数字将是质数。

这是为什么?

要了解原因,请考虑如何工作。您首先要测试2是否将数字除。如果是这样,则将尽可能多地分割2的副本,这意味着,如果以后尝试除以2的倍数的任何数字,则会发现较大的数字不是除数。

类似地,然后您将测试3是否将数字除。如果是这样,您将除以3的所有副本,因此不会是除以3的倍数的数字。

此更改无需使用is_prime函数,从而节省了大量工作。另外,可以保证您以这种方式找到的任何除数都是质数。

优化2:尽早停止

您当前的代码通过在候选除数大于目标数一半时立即停止搜索除数来工作。如果您通常在寻找除数,这是您可以做的最好的事情。但是,如果您首先进行上述优化,则可以在此之前停止。

以上将您遇到的所有主要因素明确分开的策略还有一个额外的好处。假设在完成所有除法之后,您剩余的目标编号为n。现在,假设您当前的除数为d,并且d <n。如果d除以n,则可以将n写为两个数字dn / d的乘积。因为您一直用目标数除以遇到的所有素数,所以我们保证n的素数不小于d。相应地,这意味着如果n / d <d,则d不能是n的除数。为什么?因为如果d确实除以n,那么数字n / d的素数必须小于d,但是我们知道n没有这样的主要因素。

因此,当您尝试除数时,您可以在n / d <d时停止,或者等效地在n <{{1} } 2 。一旦发生这种情况,您就会知道d不再没有素数,因此剩余数字n是最后一个素数。

在实践中,这将大大加快速度。您的目标数字大约是10 12 ,并且只要最后一个约数大约等于该数字的平方根即可停止,即大约10 6 。这意味着您只需要搜索一百万个因数,而不是一万亿个!

优化3:智能选择除数

以上两个优化或多或少反映了您的原始策略,可能足以让您获得答案并每天称呼它。但是,如果您只是出于娱乐目的想加快速度,则可以考虑尝试更好地选择除数。

例如,现在,您尝试除以目标的数字中有一半是偶数。除2以外,没有偶数是质数,因此您可以考虑将循环分为两部分:一种特殊情况,用于处理2,另一循环从3开始计数,步长为2(3、5、7, 9、11、13等),而不是大小为1的步长。(盯着目标数字,您会发现它不是偶数,因此您甚至可以完全跳过除以2!)

更好的是,考虑下载最多约10 7 的所有质数的列表。可以将该列表硬编码到您的程序中,或者将其全部转储到文本文件中并在程序启动时读入。然后,仅将目标除以该列表上的数字。瞧!现在,您只需除以质数即可,从而节省了大量时间和精力。 (素数定理说,只有大约10个ln 10 7 ≈18.4个数是质数,因此很可能会给您额外20倍的加速一切。

希望这会有所帮助!