【问题标题】:"is prime" algorithm runtime“是素数”算法运行时
【发布时间】:2013-02-14 15:58:59
【问题描述】:

我正在尝试制定最快的算法来确定一个数字是否为素数。 我对从 3 到 100,000 的每个数字执行此操作。

   for(int i = 3; i < 100000; i += 1)
        if(isPrime(i))
            System.out.println(i);

这需要 0.52 秒。 我的朋友建议不要迭代偶数:

for(int i = 3; i < 100000; i += 2)
        if(isPrime(i))
            System.out.println(i);

它需要 0.53 秒(可能是随机差异)。

为什么他的建议不减少运行时间? 如果我迭代更少的数字,我希望程序运行得更快。

isPrime() 的代码:

public static boolean isPrime(int n)
{
    if((n % 2 == 0 && n != 2) || (n % 3 == 0  && n != 3)|| (n % 5 == 0 && n != 5))
        return false;
    for(int i = 5; i < n / 5; i += 2)
    {
        if(n % i == 0)
            return false;
    }
    return true;
}

【问题讨论】:

  • 用seiving找到sqrt(MAX_INPUT)的素数,并测试从2到sqrt(MAX_INPUT)的所有素数。但是,如果您要在一个很大的范围内搜索素数,那么只需搜索到 MAX_INPUT。
  • @nhahtdh 我知道那个解决方案,但我可以问一下它为什么有用吗?
  • @shohamh:它做的工作更少,这就是它更快的原因。 (这里只讨论算法。在这种情况下,打印可能比算法本身花费更多的时间)。
  • @RudolphEst:这些天测试 100000 个数字已经不算什么了。写入控制台是一个更大的问题......

标签: java algorithm


【解决方案1】:

1) 您是否考虑过使用埃拉托色尼筛法来寻找素数?

2) 你不应该用运行时间来衡量效率

3) 你的 if 语句是素数,如果你检查的是 n = {2,3,5} 使用它来返回结果,这将阻止你执行其余的代码(在这种情况下不会节省太多时间)

4) 你的 for 循环,你可以为 i 从 7 (你已经检查过的 5 的条件,并且 6 不是素数)到 sqrt(n) 做它

【讨论】:

    【解决方案2】:

    除了算法问题之外,您的瓶颈很可能是写入System.out 流。这是一项耗时的任务,我认为您应该将该部分排除在基准循环之外。要快速测试这一点,只需将其注释掉(使用相应的if 语句):

    int iterations=100000;
    long time = System.nanoTime();
    for(int i = 3; i < 100000; i += 2) { //notice: always use curly brackets!!!
        isPrime(i);
    }
    long endTime = System.nanoTime();
    System.out.println("Time to go through " + iterations + " iterations: " + (endTime>time?endTime-time:endTime+time));
    //notice: nanoTime might turn around, resulting in smaller (negative) endTime value
    

    此外,Thomas 的回答对System.out.print 问题的回答更加详细,并且还提供了连接大量字符串的适当方法。

    算法问题:

    如果您采用 Erastothenes 筛法,则在搜索下一个质数时,您已经找到了所有较小的质数。所以你应该存储它们,而不是检查每一个大于 5 的奇数,你只需要检查那些你已经拥有的。

    同时,您不必检查所有这些,只需检查小于或等于您的数字平方根的那些:

    //suppose we have a List<Integer> primeList (populated by previous execution loops)
    // and Integer numberTested as the number under testing
    
    for(int i=0; i<primeList.size();i++) {
        if(numberTested%primeList.get(i)==0) {
            //divider found, not prime
            break;
        }        
    
        if(primeList.get(i)>Math.sqrt(numberTested)) {
            //still not found a divider -- we found a prime
            primeList.add(numberTested);
            break;
        }
    }  
    

    这有什么问题? Math.sqrt 是一项昂贵的操作。比乘法成本高得多...因此,如果数字范围允许(您必须始终牢记这一点!),我们可以使用乘法来更快地进行比较:

    sqrt(a)&gt;b === a*a&gt;b 假设 a 和 b 都是正整数。

        Integer currentPrimeSquare = primeList.get(i)*primeList.get(i);
        if(currentPrimeSquare>numberTested) {
            //still not found a divider -- we found a prime
            primeList.add(numberTested);
            break;
        }
    

    为了进一步调整它,你也可以存储素数的平方 - 如果你有足够的内存......

    【讨论】:

    • 但是System.out 仍然存在于 OP 的两个案例中。
    • @NishantShreshth 这就是为什么我提到“将该部分排除在基准循环之外”。
    【解决方案3】:

    如果您对迄今为止已知的最有效算法感兴趣,那么您应该考虑阅读AKS primality test,它以多对数时间运行。

    (这个答案基本上是another answer对素性测试问题的重申。)

    【讨论】:

      【解决方案4】:

      在比较 java 运行时时需要担心 VM 本身的开销(例如,每次执行时 println 可能会占用不同的时间)。要正确比较运行时,请将每个运行时运行一千次并获取平均值以获得更准确的结果。

      此外,在 Java 中实现 Sieve of Erastothenes 并不难,而且会很快得到结果,尽管它会占用更多内存。

      【讨论】:

        【解决方案5】:

        也许this的文章可以帮到你:

        boolean isPrime(int n) {
        //check if n is a multiple of 2
        if (n%2==0) return false;
        //if not, then just check the odds
        for(int i=3;i*i<=n;i+=2) {
            if(n%i==0)
                return false;
        }
        return true;
        }
        

        【讨论】:

        • 这如何解决实际问题?你知道,OP的文本后面有问号的部分吗?
        • 这个“改进”版本报告说 2 不是质数。如果它给出错误的答案,速度并不重要......
        【解决方案6】:

        很可能大部分时间都需要将素数打印到控制台。因此,如果素数的数量没有减少,那么减少测试的数量不会显着影响程序的速度。

        尝试将素数收集到一个字符串中并打印一次,如下所示:

        StringBuilder b = new StringBuilder();
        for(int i = 3; i < 100000; i += 1)
            if(isPrime(i))
              b.append(i).append("\n");
        
        System.out.println(b.toString());
        

        【讨论】:

        • +1 这解释了System.out 可能对输出产生的影响。
        猜你喜欢
        • 2020-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多