【问题标题】:Stack overflow error in Java recursionJava递归中的堆栈溢出错误
【发布时间】:2015-08-19 14:01:23
【问题描述】:

我正在尝试实现一个返回 200 万以下所有素数之和的代码。我有一个 isPrime(int x) 方法,如果数字是素数,则返回 true。这里是:

public static boolean isPrime(int x) {

        for (int i = 2; i < x; i++) {

            if (x % i == 0) {
                return false;
            }

        }
        return true;

    }

而我尝试递归实现的另一种方法只能在某个数字超过该数字之前有效,并且我得到堆栈溢出错误。我让代码工作的最高价格是 10,000。

这里是:

public static int sumOfPrimes(int a) {

    if (a < 2000000) {  //this is the limit

        if (isPrime(a)) {
            return a + sumOfPrimes(a + 1);

        } else {
            return sumOfPrimes(a + 1);
        }

    }
    return -1;
}

那么为什么当数字变大时会出现堆栈溢出错误,我该如何处理呢? 另外,您通常如何处理为如此大的数字编写代码? IE:像这样的正常数字运算但对于更大的数字?我递归地写了这个,因为我认为它会更有效率,但它仍然不起作用。

【问题讨论】:

  • 您听说过 Erathostene 的筛子吗?
  • 你有什么理由不使用循环吗?你不可能有这么大的递归深度。你会填满你的整个堆栈,这就是你得到堆栈溢出异常的原因。
  • @XaverKapeller 我尝试使用循环但问题是当我尝试 200 万时,什么也没发生。它试图完成代码,但需要很长时间。这就是我切换到递归的原因。
  • 你甚至没有真正使用递归......你知道你的代码实际上在做什么吗?我非常怀疑。我花了几分钟来理解它,所以我对你遇到问题并不感到惊讶。只要说这个程序没有做你认为它正在做的事情就足够了。
  • 这甚至不是真正适合递归的问题。无论您对循环有什么问题,这都是开始的地方。您现在所拥有的任何解决方案都非常遥远,解决这个问题不是要走的路。

标签: java stack overflow


【解决方案1】:

你的isPrime 函数效率低下,不必去x,去x 的平方根就够了。

但这不是您的解决方案不起作用的原因。递归深度不能为 100 万。

我会迭代地解决这个问题,使用sieve of eratosthenes 和for 循环遍历生成的boolean 数组。

【讨论】:

    【解决方案2】:

    一般来说,如果你还想使用递归,你可以使用尾递归。 在递归中,每个函数调用都会将一些数据推送到堆栈,这是有限的,从而产生堆栈溢出错误。在尾递归中,您不会向堆栈推送任何内容,因此不会抛出异常。

    基本上,您只需要将先前计算的数据作为参数发送,而不是将其放在堆栈中。

    所以:

    function(int x) {
        // end condition
        return function(x - 1) + x;
    }
    

    尾递归将是

    function (int max, int curr, int prev, int sum) {
        if (curr > max) 
            return sum;
    
        return function (max, curr + 1, curr, sum + curr)
    }
    

    请记住,这只是伪代码而不是真正的 java 代码,但与 java 代码足够接近。

    更多信息请查看

    What is tail recursion?

    【讨论】:

    • Java 不支持尾递归。
    【解决方案3】:

    使用埃拉托色尼筛法:-

    以下是通过 Eratosthenes 的方法找到小于或等于给定整数 n 的所有素数的算法:

    1) 创建一个从 2 到 n 的连续整数列表:(2, 3, 4, ..., n)。
    2) 最初,令 p 等于 2,即第一个素数。
    3) 从 p 开始,以 p 为增量向上计数,并在列表中标记每个大于 p 本身的数字。这些数字将是 2p、3p、4p 等;请注意,其中一些可能已被标记。
    4) 找出未标记的列表中第一个大于 p 的数。如果没有这样的号码,请停止。否则,现在让 p 等于这个数(即下一个素数),然后从第 3 步开始重复。

    public static void main(String[] args) {
        int n = 30;
        System.out.printf("Following are the prime numbers below %d\n", n);
        SieveOfEratosthenes(n);
    }
    
    static void markMultiples(boolean arr[], int a, int n)
    {
        int i = 2, num;
        while ( (num = i*a) <= n )
        {
            arr[ num-1 ] = true; // minus 1 because index starts from 0.
            ++i;
        }
    }
    
    // A function to print all prime numbers smaller than n
    static void SieveOfEratosthenes(int n)
    {
        // There are no prime numbers smaller than 2
        if (n >= 2)
        {
            // Create an array of size n and initialize all elements as 0
            boolean[] arr=new boolean[n];
            for(int index=0;index<arr.length-1;index++){
                arr[index]=false;
            }
    
            for (int i=1; i<n; ++i)
            {
                if ( arr[i] == false )
                {
                    //(i+1) is prime, print it and mark its multiples
                    System.out.printf("%d ", i+1);
                    markMultiples(arr, i+1, n);
                }
            }
        }
    }
    
    Output:-
    Following are the prime numbers below 30
    2 3 5 7 11 13 17 19 23 29 
    

    【讨论】:

      猜你喜欢
      • 2016-12-30
      • 2018-03-10
      • 2014-08-27
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 2015-12-08
      • 2015-06-17
      • 1970-01-01
      相关资源
      最近更新 更多