Java 试过';s#14再次使用递归,没有';我不工作。扰流器

Java 试过';s#14再次使用递归,没有';我不工作。扰流器,java,eclipse,Java,Eclipse,我早些时候发布了一篇文章,试图对其进行暴力攻击,但没有成功。下面是我对递归的尝试#2(第一次使用递归方法)。请帮忙 发生的情况如下:代码运行良好,数值较小,但当我们达到一百万时,代码就会运行,而不会发生任何事情。在Eclipse中,它仍然为我提供了结束的选项,但我让它运行了很长一段时间,没有任何帮助 /** * The following iterative sequence is defined for the set of positive * integers: * * n → n/2 (

我早些时候发布了一篇文章,试图对其进行暴力攻击,但没有成功。下面是我对递归的尝试#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);
    }

}

您的代码有许多错误之处:

  • 使用一种递归方法,这种方法不比向下循环少
  • 静态变量的使用
  • 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);
    }
}

主要问题是,您正试图使用
int
s对大数进行数学运算。当您的程序运行到
999167
desiredMax
时,您将进入一个无限循环

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

...
1330496806
665248403
1995745210
997872605
-1301349480    <-- oops
-650674740
-325337370
...
您可以尝试切换到更大的数值表示(
long
),但是,即使您切换到使用
long
值,您尝试递归的方式也会导致堆栈溢出,而您还没有完成对
1000000
所需最大值的计算。(在我的盒子上,当我转到
997474
时,我得到一个
StackOverflowError


你需要回到过去,重新思考你的课程结构。递归可能是一个有用的工具,但除非你知道自己不会走得太深,否则使用它是危险的。

这是一个很好的例子,说明你可以在哪里使用递归

下面是一个使用递归的解决方案,但它避免了需要不断重复已经计算过的路径

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

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);
    }
}
公共类Euler14{
静态长[]记录=新长[1000000];
// //////////////////////////////////////////////
//递归计算一条链的长度
//
静态长getLength(长n){
//终止条件
如果(n==1){
返回n;
}
//我们已经计算过了吗?
if((nmaxLength){
maxLength=此长度;
maxStart=i;
}
}
返回maxStart;
}
公共静态void main(字符串[]args){
长启动=System.currentTimeMillis();
System.out.println(calculatequestion14());
System.out.println(System.currentTimeMillis()-start);
}
}

调试问题时,您发现了什么?嗯,这真的是Euler n°7还是n°14?不管是什么,它看起来非常复杂(循环+递归),而我刚刚检查了问题7和14的解决方案,它们分别有15行和3行(Java和Scala),这是7行还是14行?7是寻找素数我编辑了你的问题,因为它确实是问题14,而不是问题7。还添加了对问题的描述,作为注释,它不是无限递归(这不可能发生,因为最终会使堆栈溢出)。他陷入了无限算术循环中的
doMath(999167)
的内部while循环中。看看这是否足够快会很有趣。当我解决这个问题时,我使用了记忆,所以我不想不断地重新计算子链。@AndrewShepherd是的,这是一个残酷的过程(但是它不使用内存,这可能是一件好事),但它在501毫秒内运行,这足以解决Euler问题。你还记得你的代码有多快吗?@Dici-我是用Javascript写的,不是Java,但在Chrome浏览器中运行需要120毫秒。@AndrewShepherd好的,所以在Java中它应该相当快,不编译,当我修复它时,也不起作用。您有一个
ArrayOutOfBoundsException
,您的
length
检查是无用的,因为您随后执行
records[n]=length
而不是
records[length]=n
允许我修复编译错误,因为它现在既不是Javascript也不是Java。现在运行正常,32毫秒+1秒
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);
    }
}