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 sup>. (Un nombre premier est un entier supérieur à celui qui n'est divisible que par lui-même et un.) Voici mon programme actuel: p> Ça fonctionne bien avec la recherche, Dites le nombre de 100 TH sup>, 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? P> p>
13 Réponses :
Je pense que le problème est que vous appelez de manière récursive Prime code> 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 code> 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. P>
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.
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.) P>
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. P>
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 p>
http://docs.sun.com/source/817 -2180-10 / PT_CHAP5.HTML P>
pour certaines des options Java VM. P>
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.
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 ++; code> avec le numéro
+ = 2; code> et commencez à vérifier à partir de
3 code> dès que même des numéros avec exception pour
2 code> ne sont pas prêts. P>
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. P>
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 i> 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 code> 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.
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. P>
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 ... p>
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.
Votre stratégie pour tester une prime est de vérifier sa divisibilité avec chaque nombre naturel plus petit. p>
Si vous modifiez votre stratégie pour tester la divisibilité avec juste chaque plus petit, vous économiseriez beaucoup de calcul. p>
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 i> lot, non. Il y a ~ n / journal (n) code> des nombres premiers ci-dessous
n code>, vous enregistrez donc uniquement un
log (n) code> facteur. Démarrage des tests avec
Prime (n, plancher (sqrt (n + 1))) code> otoh, serait i> sauvegarder beaucoup (comme points de vatinés). En commençant par
Prime (N, 2) CODE> 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.
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. Peu d'améliorations simples: Selon ce ESP | n> = 6 = n * (log (n) + journal (journal (n))); ESP (10001.0) == 114319.21 Code>, donc
int maximlimit = 114320; code> suffit. 2 est primitif a priori. Les autres Evens sont composites. Donc, commencez par
un nombre longofprimoignes = 1; code>, boucle avec
pour (int i = 3; i
pour ( int j = i * i; j
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. p>
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); } }
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.
Le problème réside dans le de la fonction 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 Les problèmes algorithmiques commencent que là-bas. Le code 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 ou dans pseudocode em> (réellement, haskell), le code d'origine est p> Le correctif proposé: p> 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".
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>
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>
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>
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
en C ... Vous pouvez faire une version plus courte, mais quoi que ce soit: d .. strong>
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); }
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! :)