【问题标题】:Too long runtime of euler project #14 in JavaJava中euler项目#14的运行时间太长
【发布时间】:2013-04-24 15:01:39
【问题描述】:

我的 Euler Project 14 代码如下。我已经运行这段代码超过 3 个小时,输出结果似乎是无限的。当我测试单个数字如 11、27 时,它会快速输出 collat​​z 链号:14 和 111。但是我不知道为什么它不能输出1000000个数的最大链数。

/*Problem 14
 * The following iterative sequence is defined for the set of positive integers:
 *          n -> n/2 (n is even)
 *          n -> 3n + 1 (n is odd) 
 * Using the rule above and starting with 13, we generate the following sequence:
 *               13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
 * It can be seen that this sequence (starting at 13 and finishing at 1) contains 
 * 10 terms. Although it has not been proved yet (Collatz Problem), it is thought 
 * that all starting numbers finish at 1.
 * 
 * Which starting number, under one million, produces the longest chain?
 * 
 * NOTE: Once the chain starts the terms are allowed to go above one million.
 */
package number;  
public class LongestCollatzSequence {
    public static int collatzNum(int n){
        int count = 0;
        while(n != 1){
            if(n%2 == 0)
                n = n/2;
            else 
                n = 3*n + 1;
            count++;
        }
        return count;
    }
    public static void main(String[] args) {
        int max = 0;
        for(int i = 10; i <= 1000000; i++){
            if(collatzNum(i) > collatzNum(max))
                max = i;
        }
        System.out.println(max);
    }
}

另外,我把for(int i = 10; i &lt;= 1000000; i++)转成for(int i = 10; i &lt;= 10; i++),这么短的数字列表,它仍然一直在运行,没有输出。我的代码有什么问题?

这是另一个人的决议,rianjs.net代码是:

public class Problem014
{
    public static void main(String[] args)
    {
        long begin = System.currentTimeMillis();
        LinkedList<Long> list = new LinkedList<Long>();
        long length = 0;
        int res = 0;
        for(int j = 10; j < 1000000; j++){
            long i = j;
            while (i != 1){
                if (i % 2 == 0){
                    i /= 2;
                    list.add(i);
                }
                else{
                    i = (3 * i) + 1;
                    list.add(i);
                }
            }
            if(list.size() > length){
                length = list.size();
                res = j;
            }
            list.clear();
        }
        long end = System.currentTimeMillis();
        System.out.println(res+" with chain size: "+ length);
        System.out.println(end-begin + "ms");
    }
}

他的代码运行良好,我想知道为什么我的代码无法得到输出,以及这两种分辨率的区别是什么?

【问题讨论】:

  • 您确认您的任何操作都不会导致溢出吗?这往往是这个 PE 问题的常见问题。

标签: java algorithm collatz


【解决方案1】:

collatzNum 函数的参数 int n 将溢出值 113383。如您所见,其他实现使用 long 变量作为临时值。

除此之外,您可以通过不为每个循环迭代计算 collatzNum(max) 来优化您的代码,但仅当 max 实际发生变化时,在这种情况下,您已经将新的 collatzNum(max) 作为来自 @ 的返回值987654325@.

【讨论】:

  • 嗨 jarnbjo~ 按照你的提示,我找到了我的问题,非常感谢!
【解决方案2】:

您每次都在重新计算迄今为止找到的最长数字的长度。如果你只记得迄今为止你见过的最长的一个,它会减少到几秒钟。

顺便说一句,如果你想走得更快,你可以使用所有以前的值的记忆。

static final short[] cache = new short[1000000];

public static int collatzNum(long n) {
    if (n == 1) return 1;
    if (n < cache.length) {
        int count = cache[((int) n)];
        if (count > 0)
            return count;
    }
    int next = (n & 1) == 0
            ? 1 + collatzNum(n >> 1)
            : 2 + collatzNum((n * 3 + 1) >> 1);
    if (n < cache.length)
        cache[((int) n)] = (short) next;
    return next;
}

static {
    for (int i = 10; i < cache.length; i *= 2)
        collatzNum(i - 1);
}

public static void main(String... ignored) {
    long start = System.nanoTime();
    int maxCount = 0, max = 0;
    for (int i = 1; i < 1000000; i++) {
        int count = collatzNum(i);
        if (count > maxCount) {
            maxCount = count;
            max = i;
        }
    }
    long time = System.nanoTime() - start;
    System.out.println("count=" + maxCount + ", value=" + max + ", took=" + time / 1000 / 1000.0 + " ms.");
}

在我的笔记本电脑上大约需要 19 毫秒。

注意:odd * 3 + 1 的结果总是偶数,所以你知道你可以将它除以 2。

【讨论】:

  • 嗨,彼得!你的解决方案真是太棒了!!!我试过别人的工具,你的是我见过最快的^_^非常感谢!
  • @mitcc 我进一步优化了它。立即尝试。
  • 缓存[1000000]只是保存collat​​zNum 1到1000000。而且有很多大于1000000的数字需要计算,如你所见(999999*3+1)/2超过1000000,所以不保存,每次调用时都需要计算。我对吗? ( 你的代码 [2 + collat​​zNum((n * 3 + 1) >> 1)] 真的很酷。我学到了很多,哈哈~再次感谢!
  • @mitcc: many numbers whose value is larger than 1000000 needed to be computed 我尝试对我的解决方案进行一些测试:仅缓存“奇数结果”,确定前(半)百万(奇数)个长度将 293698 个长度输入到我的缓存中,其中9138 用得上,73 最多两次:回报小于投资。
  • static { [fill sizable cache] },然后是时间缓存使用 - 哈。 (糟糕 - 我读到了 += 2。)collatzNum((n * 3 + 1) &gt;&gt; 1) - (n&gt;&gt;1) * 3 + 2 稍后溢出。 {(3(偶数+1)+1)/2 = (3偶数+3+1)/2 = 3(偶数/2)+2 = 3*(n>>1)+2}
【解决方案3】:

这是另一种解决方案。它没有彼得的答案那么有效,但更容易理解:

public class P14 {

    public static void main(String[] args) {
        getMaxLength(1000000);
    }

    private static void getMaxLength(int threshold) {
        long maxLength = 1;
        int nr = -1;
        for (int i = 3; i < threshold; i++) {
            long sequenceSize = getSequenceSize(i);
            if (sequenceSize > maxLength) {
                maxLength = sequenceSize;
                nr = i;
            }
        }
        System.out.println(nr);
    }

    private static long getSequenceSize(int n) {
        long x = n;
        long count = 1;
        while (x > 1) {
            if ((x & 1) == 0) {        // x%2==0
                x = x >> 1;            // x=x/2
                count++;               // count=count+1
            } else {
                x = x + x + x + 1;     // x=3*x+1
                x = x >> 1;            // x=x/2
                count+=2;               
            }
        }
        return count;
    }

}

时间:1.5 秒(Peter 的解决方案在我的机器上需要 0.142 秒)

使用以下观察可以显着改进:如果我们有x2*x 将在序列中多一步(因为2*x 是偶数)。这就是为什么我们可以忽略小于二分之一的数字。所以,使用下面的getMaxLength 方法:

    private static void getMaxLength(int threshold) {
        long maxLength = 1;
        int nr = -1;
        int start = (threshold>>1) - 1;      // start = threshold/2 - 1
        for (int i = start ; i < threshold; i++) {
            long sequenceSize = getSequenceSize(i);
            if (sequenceSize > maxLength) {
                maxLength = sequenceSize;
                nr = i;
            }
        }
        System.out.println(nr);
    }

计算大约需要 0.8 秒

【讨论】:

    【解决方案4】:

    这里是使用迭代和递归混合的问题的另一种解决方案。希望对您有所帮助,我还在代码中提供了一些 cmets 以帮助更好地理解它。

    private static int currentLongestChain = 1;
    
    private static long number = 0;
    
    public static void main(String[] args) {
        //TimeTable();
        PE_Problem_14();
    }
    
    private static void PE_Problem_14(){
        int longestChain = 1; // store longest chain
        for(long currentNumber = 1; currentNumber < 1000000; currentNumber++){
             if(currentNumber % 2 == 0){
                PE_Problem_14_even(currentNumber); // if currentNumber is even then invoke even helper method
             }else{
                PE_Problem_14_odd(currentNumber); // if currentNumber is odd then invoke odd helper method
             }
    
             if(currentLongestChain > longestChain){
                longestChain = currentLongestChain;  // update longestChain
                number = currentNumber;
                currentLongestChain = 0;
             }else{
                currentLongestChain = 0;
            }
        }
        System.out.println("longest chain: " + longestChain + " number: " + number); // print the result to the console screen
    }
    
    private static void PE_Problem_14_even(long evenNumber) {  // recursive method
        if(evenNumber <= 1) return;
        long localResult = (evenNumber/2);
        if(localResult % 2 != 0){
            ++currentLongestChain;
            PE_Problem_14_odd(localResult);
        }else {
            ++currentLongestChain;
            PE_Problem_14_even(localResult);
        }
    }
    
    private static void PE_Problem_14_odd(long oddNumber){  // recursive method
        if(oddNumber <= 1) return;
        long localResult = (3*oddNumber)+1;
        if(localResult % 2 == 0){
            ++currentLongestChain;
            PE_Problem_14_even(localResult);
        }else{
            ++currentLongestChain;
            PE_Problem_14_odd(localResult);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 1970-01-01
      相关资源
      最近更新 更多