7
votes

Overflow de pile lors du calcul du 10 001ème numéro principal en Java

Je fais le problème 7 du projet Euler. Ce que je suis censé faire, c'est calculer le numéro principal de 10 001 st . (Un nombre premier est un entier supérieur à celui qui n'est divisible que par lui-même et un.)

Voici mon programme actuel: xxx

Ça fonctionne bien avec la recherche, Dites le nombre de 100 TH , mais en cours d'exécution avec de très grandes entrées (telles que 10 001) entraîne un débordement de pile. Pourquoi, et comment puis-je résoudre ce problème?


2 commentaires

Ceci est juste un commentaire de style. La convention Java standard doit commencer la méthode (fonction) des noms avec une minuscule. C'est-à-dire que "Prime" devrait être "Prime".


La première règle sur le projet Euler est: vous ne parlez pas de projet Euler! :)


13 Réponses :


30
votes

Je pense que le problème est que vous appelez de manière récursive Prime pour déterminer si un numéro est prime ou non. Donc, pour déterminer si le nombre 1000 est prime ou non, vous appelez Prime 1000 fois récursivement. Chaque appel récursif nécessite que des données soient conservées sur la pile. La pile n'est que si grande, alors finalement, vous manquez de pièce sur la pile pour continuer à effectuer des appels récursifs. Essayez d'utiliser une solution itérative au lieu d'une solution récursive.


1 commentaires

Et c'est aussi beaucoup dans l'esprit du projet Euler: les laisser comprendre eux-mêmes, avec quelques indices, plutôt que de leur donner explicitement une réponse.



0
votes

Pour résoudre ceci en général, vous allez devoir passer d'une solution récursive à une itérative. (Chaque algorithme récursif peut également être exprimé itérativement.)

Depuis la fonction La fonction est récursive, il y aura toujours une limite de système sur le nombre de fois qu'il peut s'appeler lui-même.

Cependant, vous pouvez avoir suffisamment de mémoire sur votre système pour atteindre 10001. Java vous permet de définir la quantité maximale de mémoire (pile, tas, etc.) que le VM utilise. Augmentez le numéro de mémoire de la pile, et vous serez probablement capable de le faire. Voir cette page

http://docs.sun.com/source/817 -2180-10 / PT_CHAP5.HTML

pour certaines des options Java VM.


1 commentaires

La mémoire de tas n'est pas le problème, c'est cette pile qui déborde et la pile ne vit pas dans le tas. Cela étant dit, la taille de la pile de fil de la machine virtuelle peut également être configurée.



4
votes

Vous devez enregistrer tous les numéros premiers que vous avez obtenus jusqu'à présent dans une liste de recherche, vous vérifierez donc si le numéro est divisé par les numéros de cette liste. Sinon c'est un nombre premier - allez-le ajouter à la liste.
Une autre idée est de remplacer numéro ++; avec le numéro + = 2; et commencez à vérifier à partir de 3 dès que même des numéros avec exception pour 2 ne sont pas prêts.


0 commentaires

2
votes

J'ai récemment résolu ce problème. Je suggérerais de générer vos nombres premiers avec un Sieve of Eratosthenes , disons tous les premiers <1 million. Ce n'est pas un algorithme difficile à mettre en œuvre et il est assez rapide pour le nombre de nombres premiers dont vous avez besoin.


3 commentaires

Vous n'êtes pas obligé d'aller jusqu'au million d'habitants: il a été prouvé que le nième Prime a une limite supérieure de N ln (n) + n ln (ln (n)) + 2 * N (Bit.ly/93IXB7), vous pouvez donc calculer jusqu'à cette limite et compter simplement les nombres premiers que vous trouvez jusqu'à ce que vous atteigniez le numéro de premier mot de choix.


Ou vous pouvez générer des nombres premiers paresseusement, tels que, lorsque le N TI est demandé, il est calculé alors et là et enregistré pour plus tard. Ensuite, vous pouvez simplement demander le 10 001ème premier.


Et il y a beaucoup d'autres problèmes d'Euler de projet où vous avez besoin de beaucoup de nombres premiers, un tamis d'eratosthènes bien implémenté en vaut donc la peine.



2
votes

compilateurs pour certaines langues (par exemple, de nombreuses langues fonctionnelles et semi-fonctionnelles telles que LISP) convertiront la récursion de la queue comme si vous l'utilisiez dans l'itération, mais (apparemment) Le compilateur Java ne le fait pas pour vous. En conséquence, chaque appel récursif consiste à utiliser un espace de pile et, éventuellement, vous manquez et la pile déborde.

Bien sûr, à la plupart des fins, vous souhaitez utiliser un algorithme différent - ce que vous utilisez en ce moment est plutôt affreux car ces choses vont. À tout le moins, il vous suffit de vérifier les nombres impairs jusqu'à la racine carrée du numéro que vous testez ...


2 commentaires

C'est incroyable à quel point Java en arrière - même à la version 6 - est comparé à tout le reste.


@ ראוןן: Je pense que votre caractérisation est un peu injuste. Conversion de la récursion de la queue à l'itération est courante dans quelques langues, mais beaucoup moins fréquente dans de nombreux autres. Les optimisations dans un compilateur JIT sont limitées par le fait que l'utilisateur attend pendant qu'ils courent, de sorte que certaines optimisations ont rarement un sens.



1
votes

Votre stratégie pour tester une prime est de vérifier sa divisibilité avec chaque nombre naturel plus petit.

Si vous modifiez votre stratégie pour tester la divisibilité avec juste chaque plus petit, vous économiseriez beaucoup de calcul.


2 commentaires

Et si l'on change pour comparer uniquement des chiffres, pas plus élevé que la racine carrée, il y a un juste des tests à supprimer. S'il y a un facteur supérieur à la racine carrée, il y en aura aussi un plus bas.


Pas un tout lot, non. Il y a ~ n / journal (n) des nombres premiers ci-dessous n , vous enregistrez donc uniquement un log (n) facteur. Démarrage des tests avec Prime (n, plancher (sqrt (n + 1))) otoh, serait sauvegarder beaucoup (comme points de vatinés). En commençant par Prime (N, 2) et le modifier pour compter vers le haut, économiserait aussi beaucoup de temps d'exécution, car des facteurs plus petits sont beaucoup plus souvent plus fréquents que les plus grands. Seulement, la commutation sur les primes a du sens.



8
votes

Utilisez " Sieve of Eratosthenes "

Java Source: P>

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


1 commentaires

+1. Peu d'améliorations simples: Selon ce ESP | n> = 6 = n * (log (n) + journal (journal (n))); ESP (10001.0) == 114319.21 , donc int maximlimit = 114320; suffit. 2 est primitif a priori. Les autres Evens sont composites. Donc, commencez par un nombre longofprimoignes = 1; , boucle avec pour (int i = 3; i , faites la boucle interne pour ( int j = i * i; j .



0
votes

Vous pouvez toujours utiliser le Test de primalité RABIN-MILLER . C'est un algorithme très facile à mettre en œuvre et très rapide, bien que la compréhension de la façon dont cela fonctionne est un peu plus difficile.


0 commentaires

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

0 commentaires

0
votes
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++;
        }
    }
}
this is my working answer.

0 commentaires

0
votes

Le problème réside dans le de la fonction défini em> défini (x, y) code> fonction, mais aussi dans l'algorithme utilisé. Il n'y a que beaucoup de récursion profondeur em> que le mécanisme d'appel de fonction de Java peut accueillir avant l'épuisement de la pile d'appel, provoquant l'erreur "Overflow de pile".

Il suffit de tester la divisibilité contre tout Nombres sous la racine carrée du nombre en cours de test. En termes de code OP, cela signifie de commencer non à partir de Prime (n, n-1) code>, mais plutôt de Prime (n, plancher (sqrt (n + 1))) code>. Ce changement pourrait être suffisant pour éviter l'erreur si une erreur pour cette tâche spécifique, car la profondeur de la récupération passera de 10000 à seulement 100. p>

Les problèmes algorithmiques commencent que là-bas. Le code prime (x, y) code> compte down em> strong>, teste donc le nombre de nombres plus gros en premier. Mais des facteurs plus petits sont trouvés beaucoup plus souvent; Le comptage doit être effectué à partir du plus petit facteur possible, 2 (qui est rencontré pour 50% de chiffres), up em> strud> au sqrt code> de la numéro de candidat. La fonction doit être réécrite comme un simple pendant code> en boucle à cette opportunité, aussi. P>

Prochaine amélioration facile et évidente consiste à ignorer les numéros pairs. 2 est connu pour être premier; Tous les autres Evens ne sont pas. Cela signifie démarrer la boucle de numérymofyprime = 1; Numéro = 3; code> et comptant par Number + = 2 code> pour énumérer des nombres impairs uniquement, avoir isprime (n) code> Testez leur divisibilité uniquement par le Numéros impairs em> également, dans un pendant code> en démarrant avec x = 3 code>, test pour n% x code> et comptant par x + = 2 code>. p>

ou dans pseudocode em> (réellement, haskell), le code d'origine est p> xxx pré>

Le correctif proposé: p>

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


0 commentaires

0
votes

en C ... Vous pouvez faire une version plus courte, mais quoi que ce soit: d .. xxx


0 commentaires

0
votes

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


0 commentaires