项目欧拉(P14):递归问题

时间:2008-12-17 22:51:54

标签: java recursion stack-overflow collatz

您好我正在项目Euler中处理Collat​​z序列问题(问题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;
      }
   }

}

8 个答案:

答案 0 :(得分:8)

您的问题不在于堆栈的大小(您已经记住了值),但是

  1. 序列中某些数字的大小,
  2. 32位整数的上限。
  3. 提示:

    public static int seqCount(int n)
    {
       if(hm.get(n) != null) {
           return hm.get(n);
       }
       if (n < 1) {
          // this should never happen, right? ;)
       } ...
       ...
    

    希望这足够了:)。

    P.S。你会在很多项目的欧拉问题中遇到对BigNums的需求......

答案 1 :(得分:2)

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

    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;
        }
    }

强行执行,大约需要9秒才能运行

答案 2 :(得分:1)

旁注(因为看起来你实际上并不需要针对这个问题进行尾调用优化):Java中没有尾调用优化,据我所知,JVM字节码甚至都不支持它。这意味着任何深度递归都是不可能的,你必须重构它以使用其他循环结构。

答案 3 :(得分:1)

如果您计算Collat​​z序列的大小,数字高达1,000,000 您应该重新考虑使用 整数 类型。我建议使用 BigInteger ,或者使用 long

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

答案 4 :(得分:1)

我认为你需要这两个提示:

  1. 不要使用Integer,因为在某个起始编号处,序列将飞入一些大于Integer.Max_VALUE的数字,即2147483647.使用Long代替。
  2. 尽管使用了memoization,尽量不要使用递归来解决这个问题。正如我前面提到的,一些数字会飞得很高并产生大量的堆栈,这会导致堆栈溢出。尝试使用“常规”迭代,如do-while或for。当然,你仍然可以在“常规”循环中使用一些成分,例如memoization。
  3. 哦,我忘记了什么。由于算术溢出,可能会发生堆栈溢出。由于您使用Integer,因此当算术溢出发生时,Java可能会将这些“飞行数字”“更改”为负数。并且如方法seqCount(int)中所示,您不检查不变量n&gt; 0

答案 5 :(得分:0)

您不仅可以通过递归解决此问题,还可以使用单个循环解决此问题。如果你写int,有溢出。因为它会产生很长时间的chaning并且递归永远不会结束因为永远不会等于1而你可能会得到 stackoverflow 错误

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

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));
    }
}

答案 6 :(得分:0)

在这里,您可以看看问题14的递归实现:

http://chmu.bplaced.net/?p=265

答案 7 :(得分:-1)

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
  }