再次尝试递归欧拉的#14,对我来说没有用。 SPOILERS EULER 14th

时间:2015-01-26 22:28:57

标签: java eclipse

我之前发布过试图暴力破解它,但它没有用。这是我的递归尝试#2(第一次使用递归方法)。请帮忙!

接下来会发生什么:代码运行正常,数字较小,但是当我们达到一百万时,代码就会运行,并且根本不会发生。在Eclipse中,它仍然可以让我选择结束,但我已经让它运行了很长时间而没有任何帮助。

/**
* 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.
*/
public class Euler14 {
    static int desiredMax = 1000000;
    static int maxTerm = 0;
    static int maxNumberOfTerms = 0;
    static int currentNumber = 0;
    static int numberOfTerms = 0;
    public static void doMath(int startingNumber) {
        if(startingNumber == 1) {
            System.out.print( maxTerm + " " + maxNumberOfTerms);
        }
        else {
            currentNumber = desiredMax;
            while(currentNumber!= 1) {
                if(currentNumber%2 == 0) {
                    currentNumber = currentNumber/2;
                    numberOfTerms++;
                } else {
                    currentNumber = (3 * currentNumber) + 1;
                    numberOfTerms++;
                }
            }
            numberOfTerms++;
            if(numberOfTerms > maxNumberOfTerms) {
                maxNumberOfTerms = numberOfTerms;
                maxTerm = startingNumber;
            }
            desiredMax--;
            doMath(desiredMax);

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

        doMath(desiredMax);
    }

}

3 个答案:

答案 0 :(得分:3)

您的代码存在许多错误:

  • 使用递归方法,这种方法不亚于循环向下
  • 使用静态变量
  • numberOfTerms从未重新初始化
  • 如azurefrog所指出的,你有一个整数溢出,导致无限循环。

当他提出答案时,我正在尽可能少地修改你的代码,所以我现在所做的就是向你展示一个与你的代码非常相似的工作代码。看看它有多清洁:

public class Euler14 {
    public static void main(String[] args) {
        int maxTerm = 1000000;
        int maxNumberOfTerms = 1;

        // this loop replaces your recursion, which is not needed here and quite costly even if it is tail-recursion
        for (int i = maxTerm ; i >= 2; i--) {
            int numberOfTerms = 0;
            // declare as long to prevent the overflow
            long currentNumber = i;
            while (currentNumber != 1) {
                if (currentNumber % 2 == 0)
                    currentNumber = currentNumber / 2;
                else
                    currentNumber = (3 * currentNumber) + 1;

                numberOfTerms++;
                if (numberOfTerms > maxNumberOfTerms) {
                    maxNumberOfTerms = numberOfTerms;
                    maxTerm = i;
                }
            }
        }
        System.out.println(maxTerm);
    }
}

答案 1 :(得分:3)

主要问题是你试图用int s对大数字进行数学运算。当您的程序降至desiredMax 999167时,您将进入无限循环。

在Java中,int可以表示的最大值为2,147,483,647。 当您的算法到达999167时,它会很快超过该限制。 如果在内部while循环中打印currentNumber的值,则会看到:

...
1330496806
665248403
1995745210
997872605
-1301349480    <-- oops
-650674740
-325337370
...

您正尝试将currentNumber设置为2,993,617,816,因此您的值将转为overflow

这会导致您的while循环永不终止,因为您没有考虑负数。你很快就会陷入重复的

序列
-25
-74
-37
-110
-55
-164
-82
-41
-122
-61
-182
-91
-272
-136
-68
-34
-17
-50
-25
... ad infinitum

您可以尝试切换到更大的数字表示(long),但是,即使您切换到使用long值,您尝试递归的方式也会导致堆栈溢出在您尝试评估desiredMax 1000000之前完成。 (在我的框中,当我到达StackOverflowError时,我得到997474

您需要返回并重新考虑您的计划结构。递归可能是一个有用的工具,但使用它是危险的,除非你知道你不会太深入。

答案 2 :(得分:1)

这是您可以使用Memoization的一个很好的例子。

下面是一个使用递归的解决方案,但是不需要不断地遍历你已经计算过的路径。

这也将链计算代码与搜索最大代码分开。

public class Euler14 {
    static long[]   records = new long[1000000];

    // //////////////////////////////////////////////
    // Recursively calculates one chain length
    //
    static long getLength(long n) {
        // Terminating condition
        if (n == 1) {
            return n;
        }
        // Have we already calculated this?
        if ((n < records.length) && (records[(int) n] != 0)) {
            return records[(int) n];
        }
        // Haven't calculated this yet, so calculate it now
        long length = getLength(n % 2 == 0 ? n / 2 : 3 * n + 1) + 1;
        // Record the result for later use
        if (n < records.length) {
            records[(int) n] = length;
        }
        return length;
    }

    static long calculateQuestionFourteen() {
        long maxLength = 0;
        long maxStart = 0;
        for (long i = 1; i < 1000000; ++i) {
            long thisLength = getLength(i);
            if (thisLength > maxLength) {
                maxLength = thisLength;
                maxStart = i;
            }
        }
        return maxStart;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        System.out.println(calculateQuestionFourteen());
        System.out.println(System.currentTimeMillis() - start);
    }
}