Java 欧拉计划(第14页):递归问题

Java 欧拉计划(第14页):递归问题,java,recursion,stack-overflow,collatz,Java,Recursion,Stack Overflow,Collatz,嗨,我在做欧拉计划中的科拉兹序列问题(问题14)。我的代码适用于100000以下的数字,但如果数字较大,则会出现堆栈溢出错误 有没有一种方法可以使用尾部递归重新分解代码,或者防止堆栈溢出。代码如下: import java.util.*; public class v4 { // use a HashMap to store computed number, and chain size static HashMap<Integer, Integer> hm =

嗨,我在做欧拉计划中的科拉兹序列问题(问题14)。我的代码适用于100000以下的数字,但如果数字较大,则会出现堆栈溢出错误

有没有一种方法可以使用尾部递归重新分解代码,或者防止堆栈溢出。代码如下:

import java.util.*;

public class v4
{

   // use a HashMap to store computed number, and chain size 

   static HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();

   public static void main(String[] args)
   {

      hm.put(1, 1);

      final int CEILING_MAX=Integer.parseInt(args[0]);
      int len=1;
      int max_count=1;
      int max_seed=1;

      for(int i=2; i<CEILING_MAX; i++)
      {
          len = seqCount(i);

          if(len > max_count)
          {
             max_count = len;
             max_seed = i;
          }
      }
      System.out.println(max_seed+"\t"+max_count);
   }

   // find the size of the hailstone sequence for N

   public static int seqCount(int n)
   {

      if(hm.get(n) != null)
      {
         return hm.get(n);
      }

      if(n ==1)
      {
         return 1;
      }
      else
      {
         int length = 1 + seqCount(nextSeq(n));
         hm.put(n, length);
         return length;
      }
   }

   // Find the next element in the sequence

   public static int nextSeq(int n)
   {

      if(n%2 == 0)
      {
         return n/2;
      }
      else
      {
         return n*3+1;
      }
   }

}
import java.util.*;
公共类v4
{
//使用HashMap存储计算的数字和链大小
静态HashMap hm=newhashmap();
公共静态void main(字符串[]args)
{
hm.put(1,1);
最终整数上限_MAX=Integer.parseInt(args[0]);
int len=1;
int max_count=1;
int max_seed=1;
对于(整数i=2;i最大计数)
{
最大计数=len;
max_seed=i;
}
}
System.out.println(最大种子数+“\t”+最大计数);
}
//求N的冰雹序列的大小
公共静态整数序列计数(整数n)
{
如果(hm.get(n)!=null)
{
返回hm.get(n);
}
如果(n==1)
{
返回1;
}
其他的
{
int length=1+seqCount(nextSeq(n));
hm.put(n,长度);
返回长度;
}
}
//查找序列中的下一个元素
公共静态int nextSeq(int n)
{
如果(n%2==0)
{
返回n/2;
}
其他的
{
返回n*3+1;
}
}
}

问题不在于堆栈的大小(您已经在记忆值),而在于

  • 序列中某些数字的大小,以及
  • 32位整数的上限 提示:

    公共静态int seqCount(int n)
    {
    如果(hm.get(n)!=null){
    返回hm.get(n);
    }
    if(n<1){
    //这永远不会发生,对吗?;)
    } ...
    ...
    
    希望这就足够了:)


    另外,在很多project euler问题中,您会遇到对BigNums的需求…

    旁注(因为似乎您实际上不需要对该问题进行尾部调用优化):尾调用优化在Java中不可用,据我所知,它甚至不受JVM字节码的支持。这意味着任何深度递归都是不可能的,您必须对其进行重构以使用其他循环构造。

    如果您正在计算Collatz序列的大小,最大值为1000000 您应该重新考虑使用整数类型。我建议使用大整数或可能的


    这应该可以缓解遇到的问题,但需要注意的是,根据JVM的不同,您可能仍然会耗尽堆空间。

    如果您将整数更改为,将为您提供足够的空间来解决问题。 下面是我用来回答这个问题的代码:

        for(int i=1;i<=1000000;i+=2)
        {
            steps=1;
            int n=i;
            long current=i;
            while(current!=1)
            {
                if(current%2==0)
                {
                    current=current/2;
                }else{
                    current=(current*3)+1;
                }
                steps++;
            }
            if(steps>best)
            {
                best=steps;
                answer=n;
            }
        }
    
    for(inti=1;ibest)
    {
    最佳=步骤;
    答案=n;
    }
    }
    

    暴力强制,运行大约需要9秒

    我认为您需要以下两个提示:

  • 不要使用整数,因为在某个起始数字处,序列将飞入大于Integer.Max_值2147483647的一些数字。用Long代替
  • 尽量不要使用递归来解决这个问题,即使是记忆化。正如我前面提到的,一些数字会飞得很高,并产生大量堆栈,这将导致堆栈溢出。尝试使用“常规”迭代,如do while或for。当然,你仍然可以在“常规”循环中使用一些成分,比如记忆
  • 哦,我忘了什么。堆栈溢出可能是因为算术溢出。由于您使用整数,当算术溢出发生时,Java可能会将这些“飞行数字”变为负数。正如在方法seqCount(int)中所看到的,您不需要检查不变量n>0。

    import java.util.*;
    
    import java .util.*;
    public class file 
      {
     public static void main(String [] args)
      {
       long largest=0;
       long number=0;
        for( long i=106239;i<1000000;i=i+2)
         {
          long k=1;
           long z=i;
          while(z!=1)
           {
            if(z%2==0)
            {
             k++;
             z=z/2;
            } else{
              k++;
              z=3*z+1;
               }
           }    
        if(k>largest)
          {
           number=i;
           largest=k;
           System.out.println(number+" "+largest);
          }
         }//for loop
    
       }//main
      }
    
    公共类文件 { 公共静态void main(字符串[]args) { 最大长=0; 长数=0; 用于(长i=106239;i最大) { 数字=i; 最大=k; System.out.println(数字+“”+最大值); } }//for循环 }//主要 }
    您不仅可以使用递归,还可以使用单个循环来解决此问题。如果您写入int,则会出现溢出,因为它会在更改时生成很长时间,并且递归永远不会结束,因为从不等于1,您可能会得到堆栈溢出错误

    以下是我的循环和递归解决方案:

    public class Collatz {
    
        public int getChainLength(long i) {
            int count = 1;
    
            while (i != 1) {
                count++;
    
                if (i % 2 == 0) {
                    i /= 2;
                } else {
                    i = 3 * i + 1;
                }
            }
    
            return count;
        }
    
        public static int getChainLength(long i, int count) {
            if (i == 1) {
                return count;
            } else if (i % 2 == 0) {
                return getChainLength(i / 2, count + 1);
            } else {
                return getChainLength(3 * i + 1, count + 1);
            }
        }
    
        public int getLongestChain(int number) {
            int longestChain[] = { 0, 0 };
    
            for (int i = 1; i < number; i++) {
                int chain = getChainLength(i);
    
                if (longestChain[1] < chain) {
                    longestChain[0] = i;
                    longestChain[1] = chain;
                }
            }
    
            return longestChain[0];
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            System.out.println(new Collatz().getLongestChain(1000000));
        }
    }
    
    公共类Collatz{
    公共整数getChainLength(长i){
    整数计数=1;
    而(i!=1){
    计数++;
    如果(i%2==0){
    i/=2;
    }否则{
    i=3*i+1;
    }
    }
    返回计数;
    }
    公共静态整型getChainLength(长i,整型计数){
    如果(i==1){
    返回计数;
    }else如果(i%2==0){
    返回getChainLength(i/2,计数+1);
    }否则{
    返回getChainLength(3*i+1,count+1);
    }
    }
    公共整数getLongestChain(整数编号){
    int longestChain[]={0,0};
    对于(int i=1;i
    在这里,您可以看看我对问题14的递归实现:


    对于这个特殊的问题,可以使用long。(对于后面需要大于long的代码,我使用Python而不是Java,因为它会自动将整数提升为bignums)防止堆栈溢出?你怎么敢这么说!;)Scala(编译为JVM字节码的语言)支持有限的尾部递归,所以JVM可以做到这一点,但不是用Java或非常好:)请不要添加l
    public class Collatz {
    
        public int getChainLength(long i) {
            int count = 1;
    
            while (i != 1) {
                count++;
    
                if (i % 2 == 0) {
                    i /= 2;
                } else {
                    i = 3 * i + 1;
                }
            }
    
            return count;
        }
    
        public static int getChainLength(long i, int count) {
            if (i == 1) {
                return count;
            } else if (i % 2 == 0) {
                return getChainLength(i / 2, count + 1);
            } else {
                return getChainLength(3 * i + 1, count + 1);
            }
        }
    
        public int getLongestChain(int number) {
            int longestChain[] = { 0, 0 };
    
            for (int i = 1; i < number; i++) {
                int chain = getChainLength(i);
    
                if (longestChain[1] < chain) {
                    longestChain[0] = i;
                    longestChain[1] = chain;
                }
            }
    
            return longestChain[0];
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            System.out.println(new Collatz().getLongestChain(1000000));
        }
    }