J'ai pris le problème # 12 de Project Euler comme un exercice de programmation et pour comparer mes implémentations (sûrement pas optimales) en C, Python, Erlang et Haskell. Afin d'obtenir des temps d'exécution plus élevés, je recherche le premier numéro de triangle avec plus de 1000 diviseurs au lieu de 500 comme indiqué dans le problème d'origine.
Le résultat est le suivant:
C:
lorenzo@enzo:~/erlang$ gcc -lm -o euler12.bin euler12.c
lorenzo@enzo:~/erlang$ time ./euler12.bin
842161320
real 0m11.074s
user 0m11.070s
sys 0m0.000s
Python:
lorenzo@enzo:~/erlang$ time ./euler12.py
842161320
real 1m16.632s
user 1m16.370s
sys 0m0.250s
Python avec PyPy:
lorenzo@enzo:~/Downloads/pypy-c-jit-43780-b590cf6de419-linux64/bin$ time ./pypy /home/lorenzo/erlang/euler12.py
842161320
real 0m13.082s
user 0m13.050s
sys 0m0.020s
Erlang:
lorenzo@enzo:~/erlang$ erlc euler12.erl
lorenzo@enzo:~/erlang$ time erl -s euler12 solve
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
1> 842161320
real 0m48.259s
user 0m48.070s
sys 0m0.020s
Haskell:
lorenzo@enzo:~/erlang$ ghc euler12.hs -o euler12.hsx
[1 of 1] Compiling Main ( euler12.hs, euler12.o )
Linking euler12.hsx ...
lorenzo@enzo:~/erlang$ time ./euler12.hsx
842161320
real 2m37.326s
user 2m37.240s
sys 0m0.080s
Sommaire:
- C: 100%
- Python: 692% (118% avec PyPy)
- Erlang: 436% (135% grâce à RichardC)
- Haskell: 1421%
Je suppose que C a un gros avantage car il utilise long pour les calculs et non des entiers de longueur arbitraire comme les trois autres. De plus, il n'a pas besoin de charger un runtime en premier (les autres?).
Question 1:
Erlang, Python et Haskell perdent-ils de la vitesse en raison de l'utilisation d'entiers de longueur arbitraire ou pas tant que les valeurs sont inférieures à MAXINT
?
Question 2: Pourquoi Haskell est-il si lent? Existe-t-il un indicateur de compilation qui désactive les freins ou est-ce mon implémentation? (Ce dernier est tout à fait probable car Haskell est un livre avec sept sceaux pour moi.)
Question 3: pouvez-vous me donner quelques conseils pour optimiser ces implémentations sans changer la façon dont je détermine les facteurs? Optimisation de toute façon: plus agréable, plus rapide, plus "natif" de la langue.
ÉDITER:
Question 4: Mes implémentations fonctionnelles permettent-elles le LCO (optimisation du dernier appel, également appelé élimination de la récursivité de la queue) et évitent ainsi d'ajouter des trames inutiles à la pile d'appels?
J'ai vraiment essayé d'implémenter le même algorithme aussi similaire que possible dans les quatre langues, même si je dois admettre que mes connaissances en Haskell et Erlang sont très limitées.
Codes sources utilisés:
#include <stdio.h>
#include <math.h>
int factorCount (long n)
{
double square = sqrt (n);
int isquare = (int) square;
int count = isquare == square ? -1 : 0;
long candidate;
for (candidate = 1; candidate <= isquare; candidate ++)
if (0 == n % candidate) count += 2;
return count;
}
int main ()
{
long triangle = 1;
int index = 1;
while (factorCount (triangle) < 1001)
{
index ++;
triangle += index;
}
printf ("%ld\n", triangle);
}
#! /usr/bin/env python3.2
import math
def factorCount (n):
square = math.sqrt (n)
isquare = int (square)
count = -1 if isquare == square else 0
for candidate in range (1, isquare + 1):
if not n % candidate: count += 2
return count
triangle = 1
index = 1
while factorCount (triangle) < 1001:
index += 1
triangle += index
print (triangle)
-module (euler12).
-compile (export_all).
factorCount (Number) -> factorCount (Number, math:sqrt (Number), 1, 0).
factorCount (_, Sqrt, Candidate, Count) when Candidate > Sqrt -> Count;
factorCount (_, Sqrt, Candidate, Count) when Candidate == Sqrt -> Count + 1;
factorCount (Number, Sqrt, Candidate, Count) ->
case Number rem Candidate of
0 -> factorCount (Number, Sqrt, Candidate + 1, Count + 2);
_ -> factorCount (Number, Sqrt, Candidate + 1, Count)
end.
nextTriangle (Index, Triangle) ->
Count = factorCount (Triangle),
if
Count > 1000 -> Triangle;
true -> nextTriangle (Index + 1, Triangle + Index + 1)
end.
solve () ->
io:format ("~p~n", [nextTriangle (1, 1) ] ),
halt (0).
factorCount number = factorCount' number isquare 1 0 - (fromEnum $ square == fromIntegral isquare)
where square = sqrt $ fromIntegral number
isquare = floor square
factorCount' number sqrt candidate count
| fromIntegral candidate > sqrt = count
| number `mod` candidate == 0 = factorCount' number sqrt (candidate + 1) (count + 2)
| otherwise = factorCount' number sqrt (candidate + 1) count
nextTriangle index triangle
| factorCount triangle > 1000 = triangle
| otherwise = nextTriangle (index + 1) (triangle + index + 1)
main = print $ nextTriangle 1 1
Euler12[x_Integer] := Module[{s = 1}, For[i = 2, DivisorSigma[0, s] < x, i++, s += i]; s]
. Hourra!