Le problème réside dans la récursivement fonction définie Prime(X,Y)
, mais aussi dans l'algorithme utilisé. Il y a seulement autant de récursivité profondeur que le mécanisme d'appel de fonction de Java peut accueillir avant l'épuisement de la pile d'appels, provoquant l'erreur "stack overflow".
Il suffit de tester la divisibilité pour tous les nombres inférieurs à la racine carrée du nombre testé. Pour ce qui est du code OP, cela signifie qu'il ne faut pas commencer par Prime(N,N-1)
, mais plutôt par Prime(N, floor(sqrt(N+1)))
. Cette seule modification pourrait suffire à éviter l'erreur SO pour cette tâche spécifique, car la profondeur de récursivité passera de 10000 à 100 seulement.
Les problèmes algorithmiques commencent seulement là. Le code Prime(X,Y)
compte vers le bas, testant ainsi le nombre en premier. Mais de plus petits facteurs sont trouvés beaucoup plus souvent; le comptage doit être effectué à partir du plus petit facteur possible, 2 (qui est rencontré pour 50% des nombres), jusqu'à du numéro de candidat. La fonction devrait être réécrite comme une simple boucle while
à cette occasion, aussi.
Ensuite, une amélioration facile et évidente consiste à ignorer complètement les nombres pairs. 2 est connu pour être premier; tous les autres evens ne le sont pas. Cela signifie à partir de la boucle de numberOfPrimes = 1; number = 3;
et de comptage par number += 2
d'énumérer les nombres impairs seulement, ayant isPrime(N)
tester leur divisibilité que par les impairs numéros et, dans une boucle while
commençant par X = 3
, les tests pour N % X
et le comptage par X += 2
.
Ou dans pseudocode (en fait, Haskell), le code d'origine est
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
le correctif proposé:
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
détails ci sont pour le code non compilé dans GHCi (sur un ordinateur portable lent). Empirical local orders of growth pris comme log(t2/t1)/log(n2/n1)
. Encore plus rapide est le test par les nombres premiers, et non par des nombres impairs.
BTW, les impressions de code d'origine pas de prime 10001e, mais le chiffre au-dessus.
Ceci est juste un style commentaire. La convention java standard consiste à commencer les noms de méthode (fonction) avec une minuscule. Autrement dit, "Prime" devrait être "premier". – nicerobot
La première règle à propos du Projet Euler est: Vous ne parlez pas du projet Euler! :) – Noctis