项目欧拉#3 - 解决方案永远运行

时间:2015-01-08 03:03:09

标签: java algorithm primes

我第一次遇到这个问题。感觉它永远不会结束。

我的方法:

import java.util.TreeSet;

public class Euler3 {
    public static void main(String[] args) {
        long result = 0;
        long startTime = System.nanoTime();

        long numberGiven = 600851475143L;
        TreeSet<Long> nums = new TreeSet<>();

        for (long i = 2L; i < numberGiven; i++) {
            if (numberGiven % i == 0 && isPrime(i)) {
                nums.add(i);
            }
        }

        result = nums.last();   

        System.out.print("Result: " + result +
            ".\nTime used for calculation in nanoseconds: " +
            (System.nanoTime() - startTime) + ".");
    }

    public static boolean isPrime(long n) {
        if (n <= 3) {
            return n == 1 ? false : true;
        } else if (n % 2 == 0 || n % 3 == 0) {
            return false;
        } else {
            for (int i = 5; i * i <= n; i += 6) {
                if (n % i == 0 || n % (i + 2) == 0) {
                    return false;
                }
            }
        return true;
        }
    }
}

当然,这适用于较小的数字,但可能在超过6000亿的情况下似乎没有效果。 我想知道,没有给出答案:

  1. 我可以用一些明显的改动来减少 运行时间/检查是否必要?

  2. 虽然这里显然没有效果,但是这种方法 否则接受或发布此挑战的人, 即使数量较少,也要寻找不同的东西?

  3. 我第一次尝试使用整数并遇到溢出相关的错误,是否有任何类似的事情发生在实际上会阻止它终止?

2 个答案:

答案 0 :(得分:2)

对于每个号码,您正在检查这是一个因素,您正在进行内部循环以确定它是否为素数。这意味着您的算法正在有效地执行n * m操作。

您可以使用以下数学“技巧”,我认为这与UNIX factor程序使用的相同。

由于每一个数字都是素数或一组素数的唯一乘积(在集合中有潜在的重复),我们可以开始将数字除以第一个素数(实际上减少了过程中的数量)直到那不再可能(即它变得奇怪)。此时,减少的数字将不会有两个或任何两个的倍数作为一个因素。

然后我们通过连续除以3来做同样的事情,直到不再可能。

现在你认为这可能是繁重的,但是,因为你已经剥离了所有“两个”因素,这个数字不能可能是四的倍数(或任何其他偶数那件事)。因此,我们检测到并向上移动到下一个五的除数并开始除以它。

所以除法操作只对素数除数进行,大大加快了速度。此外,一旦除数超过(减少的)数的平方根,就不可能有更多的因子,所以我们退出。在这种情况下,减少的数字给出了最终(因此最高)的素数因子。

例如,请考虑数字924

Number  Divisor  Result
------  -------  ------
   924        2*    462
   462        2*    231
   231        2     not divisible, go to 3
   231        3*     77
    77        3     not divisible, go to 4
    77        4     not divisible, go to 5
    77        5     not divisible, go to 6
    77        6     not divisible, go to 7
    77        7*    11
    11*       7     stop since 7 * 7 > 11

因此924的主要因素是{2, 2, 3, 7, 11}


现在我强烈建议您在下面看之前尝试自己的算法,因为Euler的整个是测试你的自己的能力。我只是提供完整性的解决方案:

public class Test
{
    public static void main(String[] args) {
        long startTime = System.nanoTime();

        long number = 600851475143L;

        // Start with a divisor of two,
        // continue until over sqrt(number).

        long divisor = 2L;
        while (divisor * divisor <= number) {
            if ((number % divisor) == 0) {
                // If factor, output then reduce number.

                System.out.println(divisor);
                number = number / divisor;
            } else {
                // Otherwise, move to next divisor.

                divisor++;
            }
        }

        // Final number is final divisor.

        System.out.println(number);

        System.out.print("Time used for calculation in nanoseconds: " +
                (System.nanoTime() - startTime) + ".");
    }
}

这为你提供了大约五个千分之秒的四个主要因素(无论如何,在我的盒子上):

71
839
1471
6857
Time used for calculation in nanoseconds: 458826.

答案 1 :(得分:2)

程序可以像这样简单,在一秒钟内运行:

long val = 600851475143L;
long ans = 0;
for(long i = 2; i*i <= val; i++){
    if(val % i == 0){
       ans = i;
       while(val % i == 0)//This step will make sure that i is prime
           val /= i;
    }
}
if(val != 1){//If val is not 1, so val is a prime
   ans = val > ans ? val : ans;
}
System.out.println(ans);

答案是6857,这是正确答案:)

请注意,我们只检查i小于i*i的所有val值。