【问题标题】:Find Prime Number 10001 Optimization Questions查找质数 10001 优化问题
【发布时间】:2014-07-22 19:53:14
【问题描述】:

所以我创建了一个程序来查找第 10,001 个素数。这是该程序的主要内容:

int main(){

  int i;
  int j;
  int count=0;
  int currnumber=0;

  for(i=1; count < 10002; i++){

      if(isPrime(i)){

          count++;
          currnumber = i;

          if(count == 10002)
              printf("%i",currnumber);
      }


  }

}

这是我在自定义库中构建的 IsPrime 函数的代码:

long long isPrime(long long number){

    long long i = 2;

    if(number == 2)
        return 1;

    for(i=2;i<number;i++){

        if(number % i == 0)
            return 0;



    }
    if(i >= number && ((number % 1 == 0) && (number % number == 0)))
        return 1;
}

当我运行该程序时,它可以工作并给我正确的答案(这是一个欧拉问题,所以我知道我做对了 :D),但处理过程至少需要 6 秒。所以我的问题是:

  1. 为什么要花这么长时间?这与我设置算法的方式有关吗?
  2. 如何改进我的代码以使其运行得更快?

提前致谢!

【问题讨论】:

  • 查找“初筛”并编写一个简单的代码。你的做了很多不必要的测试(例如,一旦我们测试了一个数字不能被 2 整除,为什么还要测试任何偶数?)。
  • 这个问题更适合codereview.stackexchange.com
  • 试试for(i=2;i&lt;number;i++){,而不是for (i = 3; i &lt; number; i += 2) {。遍历偶数是没有意义的,因为它们无论如何都不能是素数。
  • 他不是要求找到 N 以下所有素数的最快方法,而是要求找到第 N 个素数的最快方法。这是不同的。

标签: c


【解决方案1】:

您可以做的第一件事可能是缓存您创建的素数值,并使用sieve of eratosthenes 算法,以便在找到它们后不必不断地重新计算素数值。

【讨论】:

    【解决方案2】:

    您正在使用两个 for 循环

    我想到的一种更快的方法,也是一个很好的练习,算法叫做:Sieve of Eratosthenes

    【讨论】:

      【解决方案3】:

      可以进行很多优化。一些人提到过埃拉托色尼的筛子,这是对的,但让我给你一些额外的建议。

      在不改变算法的情况下改进你的代码:正如迈克尔帕克所说,你不需要处理偶数,因为其中只有一个是素数(数字 2,这使它成为“最奇数”) ' 最重要的是——这是一个双关语)。您可以通过避免三的倍数来执行类似的技巧,归结为仅处理整数 i,它们是 6 的倍数的 1 或 5。要将其转换为代码,首先将 count 设置为 3考虑素数 2、3 和 5。然后从 6 开始 i 并测试 i+1i+5(都在 for 循环内),然后每次将 i 递增 6。

      对于isPrime(),您可以添加类似的改进,但您也可以在达到数字的平方根后停止试除法。这是因为number 有一个除数 >= sqrt(number) 当且仅当它有一个除数 a除以number,那么number/a又是一个除数(剩下的细节可以填写,因为你很聪明)。

      使用埃拉托色尼筛改进您的代码: 正如许多人所提到的,埃拉托色尼筛更能解决这个问题。事实上,它的速度超级快。但一个经常被忽视的问题是,让你的筛子长度有多大,以确保你捕捉到你所追求的素数,而不会使它变得异常大? Prime Number Theorem 估计它应该有多大。但是你需要一个upper bound。建议使用 n * (ln n * ln ln n)。

      【讨论】:

        【解决方案4】:

        如果我理解正确,您将取每个小于 1001 的数字并检查它是否有共同点。这是非常缓慢的。复杂性实际上呈指数级增长。你应该做的是将这种方法与筛子结合起来。用公分母方法找到任何素数,然后将它们从 1 乘到 n,直到它们的所有倍数都在 1 到 10001 之间。这样,您将跳过对所有素数的倍数进行公分母方法的测试成立。例如,试试这个:

        ArrayList<Integer> List = new ArrayList<Integer>();
        ArrayList<Integer> Primes = new ArrayList<Integer>();
        Primes.add(2);
        Integer p=2;
        Integer n=105000;
        Integer i=1;
        
        while(p < n) {
            i=1;
            while((p*i)<=n){
                List.add(p*i);
                i++;
            }
            while (p < n){
            p++;
            if(List.contains(p)){}
            else {Primes.add(p); break;}
            }
        }
        
        System.out.println(Primes.get(10000));
        

        【讨论】:

        • 列表不是最好的数据结构。一方面,它将包含重复项,这会使您的空间爆炸——O(n^2) 内存。 Set 更接近您想要的,但是当您评估运行时间时,您需要考虑插入到集合中并检查包含的时间。归根结底,筛子的最佳数据结构只是一个数组:它既节省时间又节省内存。
        • 我使用数组列表的原因是我必须实例化一个新数组才能向其中添加元素。
        猜你喜欢
        • 2015-11-26
        • 2014-07-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多