在Java中计算第10,001个素数时堆栈溢出

时间:2010-03-21 02:23:13

标签: java stack-overflow primes

我正在做Euler项目的问题7。我应该做的是计算10,001 st 素数。 (素数是一个大于1的整数,只能被自身和一个整除。)

这是我目前的计划:

public class Problem7 {
    public static void main(String args[]) {
        long numberOfPrimes = 0;
        long number = 2;

        while (numberOfPrimes < 10001) {
            if (isPrime(number)) {
                numberOfPrimes++;
            }
            number++;
        }
        System.out.println("10001st prime: " + number);
    }

    public static boolean isPrime(long N) {
        if (N <= 1)
            return false;
        else
            return Prime(N, N - 1);
    }

    public static boolean Prime(long X, long Y) {
        if (Y == 1)
            return true;
        else if (X % Y == 0)
            return false;
        else
            return Prime(X, Y - 1);
    }
}

找到100个 th 素数时可以正常工作,但运行非常大的输入(例如10,001)会导致堆栈溢出。为什么,我该如何解决这个问题?

13 个答案:

答案 0 :(得分:30)

我认为问题在于你递归调用Prime来确定一个数字是否为素数。因此,要确定数字1000是否为素数,您将递归调用Prime 1000次。每次递归调用都需要将数据保存在堆栈中。堆栈只有这么大,所以最终你的堆栈空间不足以继续进行递归调用。尝试使用迭代解决方案而不是递归解决方案。

答案 1 :(得分:8)

使用“Sieve of Eratosthenes

Java源代码:

public class Main {
    public static void main(String args []){
        long numberOfPrimes = 0;
        int number = 1;
        int maxLimit = 10000000;
        boolean[] sieve = new boolean[maxLimit];
        for ( int i = 2; i < maxLimit; i++ ) {
            if ( sieve[i] == true ) continue;

            numberOfPrimes++;

            if ( numberOfPrimes == 10001 ) {
                number = i;
                break;
            }

            for ( int j = i+i; j < maxLimit; j += i )
                sieve[j] = true;
        }
        System.out.println("10001st prime: "+ number);
    }
}

答案 2 :(得分:4)

您应该将目前为止所有的素数保存到查找列表中,因此您将检查该数字是否除以该列表中的数字。如果不是,这是一个素数 - 请将其添加到列表中 另一个想法是将number++;替换为number += 2;,并且只要偶数为3的数字不是素数,就会从2开始检查。

答案 3 :(得分:2)

我最近解决了这个问题。我建议使用Sieve of Eratosthenes生成素数,比如所有素数&lt;百万。它不是一个难以实现的算法,而且它对于你需要的素数来说相当快。

答案 4 :(得分:2)

某些语言的编译器(例如许多函数和半功能语言,如Lisp)将转换尾部递归,就像您已经习惯进行迭代一样,但(显然)Java编译器并没有为您做到这一点。因此,每次递归调用都使用堆栈空间,最终会耗尽并且堆栈溢出。

当然,对于大多数用途,你想要使用不同的算法 - 你现在正在使用的是非常糟糕的事情。至少,您只需要检查奇数,直到您正在测试的数字的平方根...

答案 5 :(得分:1)

你测试素数的策略是用每个较小的自然数检查它的可分性。

如果你将你的策略转移到只用每个较小的素数来测试可分性,那么你将节省大量的计算。

答案 6 :(得分:1)

import java.util.*;

public class LargestPrime {
    public static boolean isPrime(long s) {
        for(long i = 2; i < s; i++) {
            if((s % i) == 0) return false;                   
        }
        return true;
    }

    public static void main(String[] args) {
        LargestPrime p = new LargestPrime();
        LinkedList<Long> arr = new LinkedList<Long>();
        for(long j = 2; j <= 999999; j++) {

            if(isPrime(j)) arr.add(j);

        }
        // System.out.println("List of Prime Number are: "+ arr);
        long t = arr.get(10001);

        System.out.println("The Prime Number At 10001st position: " + t);
    }
}

答案 7 :(得分:0)

为了解决这个问题,你将不得不从递归解决方案切换到迭代解决方案。 (每个递归算法也可以迭代表示。)

由于函数Prime是递归的,因此对它自己调用的次数总会有系统限制。

但是,您的系统上可能有足够的内存来达到10001. Java允许您设置VM使用的最大内存量(堆栈,堆等)。增加堆栈内存数量,你可能会成功。见本页

http://docs.sun.com/source/817-2180-10/pt_chap5.html

用于某些Java VM选项。

答案 8 :(得分:0)

您始终可以使用Rabin-Miller素性测试。这是一个非常容易实现的算法,速度非常快,但理解它的工作方式有点困难。

答案 9 :(得分:0)

package problems;

public class P_7 {
    /**
     * @param args
     */
    public static boolean prime(double num)
    {
        double x=2;
        double limit=(int) ((num/2)-(num/2)%1);
        while(x<=limit)
        {
            if(num%x==0)
                return false;
            x++;
        }
        return true;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int i=1;
        int j=3;
        while(i<=10000)
        {
            if(prime(j))
            {
                System.out.println(i);
                i++;
                System.out.println(j);
            }
            j++;
        }
    }
}

这是我的工作答案。

答案 10 :(得分:0)

问题在于递归定义的Prime(X,Y)函数,但也在于所使用的算法。在调用堆栈耗尽之前,Java的函数调用机制只能容纳递归深度,导致“堆栈溢出”错误。

足以测试对于所测试数字的平方根以下的所有数字的可分性。就OP代码而言,这意味着不是从Prime(N,N-1)开始,而是从Prime( N, floor( sqrt( N+1)) )开始。仅此更改就足以防止此特定任务的SO错误,因为递归深度将从10000更改为100。

算法问题只能从那里开始。 Prime(X,Y)代码计算 down ,从而首先按较大的数字测试数字。但更常见的是较小的因素;计数应该从最小可能因子2(对于50%的数字遇到), 向上 到候选数字的sqrt进行。该函数也应该在此机会重写为简单的while循环。

接下来简单而明显的改进是完全忽略偶数。已知2是素数;所有其他的都没有。这意味着从numberOfPrimes = 1; number = 3;开始循环并按number += 2向上计数仅枚举奇数,isPrime(N)仅通过奇数数字测试其可分性,在以while开头的X = 3循环中,测试N % X并按X += 2计算。

或者在伪代码(实际上是Haskell)中,原始代码是

main = print ([n | n<-[2..], isPrime(n)] !! 10000)  where   
  isPrime(n) = _Prime(n-1)  where
      _Prime(y) = y==1 || (rem n y > 0 && _Prime(y-1)) 
  -- 100:0.50s 200:2.57s 300:6.80s 10000:(projected:8.5h)
  --       n^2.4       n^2.4

建议的修复:

main = print ((2:[n | n<-[3,5..], isOddPrime(n)]) !! 10000)  where   
  isOddPrime(n) = _Prime(3)  where
         _Prime(y) = (y*y) > n || (rem n y > 0 && _Prime(y+2)) 
  -- 100:0.02s 200:0.03s 300:0.04s 5000:3.02s 10000:8.60s
  --                                       n^1.5

显示的时间是针对GHCi中的非编译代码(在慢速笔记本电脑上)。 Empirical local orders of growth被视为log(t2/t1) / log(n2/n1)。更快的是通过素数进行测试,而不是通过奇数进行测试。

btw, 原始代码不会打印出第10001个素数,而是高于它的数字。

答案 11 :(得分:0)

在C ...你可以做更短的版本,但无论如何:D ..

#include <stdio.h>
#include <stdlib.h>

prost_br(int p)
{
    int br=0;

    for(int i=2;i*i<=p;i++)
    if(p%i==0)
    br++;

    if(br==0)
    return 1;
    return 0;
}

int main()
{
    long i=1;
    int br=0;
FILE *a;

a=fopen("10001_prst_numbers.txt","w");
if(a==NULL){printf("\nError!\a\n"); exit(1);}

    while(br!=10001)
    {
        i++;
        if(prost_br(i))
        {
            br++;
            fprintf(a,"%d ",i);
        }

    }
    char s[]={"10001th prime number is: "};
fprintf(a,"\n%s %d",s,i);
fprintf(stdout,"\n10001th prime number is: %d\n\a",i);

fclose(a);
system("Pause");
}

答案 12 :(得分:0)

public class progs {

public int nthPrime(int nth) {
    int ctr = 0;
    int num = 0;
    int x = 2;
    int infinite = 15;          // initial value good for 6 prime values
    while(x < infinite) {
        boolean isPrime = true; 
        for(int i = 2; i <= x / 2; i++) {
            if(x % i == 0) {
                isPrime = false;
            }
        }
        if(isPrime) {
            System.out.println(x);  // optional
            ctr++;
            if(ctr == nth) {
                num = x;
                break;
            }
        }
        x++;
        if(x == infinite) {     // for bigger nth requested prime value
            infinite++;     
        }
    }
    return num;
}

public static void main(String[] args) {
    int ans = new progs().nthPrime(10001);
    System.out.println("nth prime number is " + ans);
}

}