Note that there are some explanatory texts on larger screens.

plurals
  1. POSpeed comparison with Project Euler: C vs Python vs Erlang vs Haskell
    text
    copied!<p>I have taken <a href="http://projecteuler.net/index.php?section=problems&amp;id=12" rel="noreferrer">Problem #12</a> from <a href="http://projecteuler.net/" rel="noreferrer">Project Euler</a> as a programming exercise and to compare my (surely not optimal) implementations in C, Python, Erlang and Haskell. In order to get some higher execution times, I search for the first triangle number with more than 1000 divisors instead of 500 as stated in the original problem.</p> <p>The result is the following:</p> <p><strong>C:</strong></p> <pre class="lang-none prettyprint-override"><code>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 </code></pre> <p><strong>Python:</strong></p> <pre class="lang-none prettyprint-override"><code>lorenzo@enzo:~/erlang$ time ./euler12.py 842161320 real 1m16.632s user 1m16.370s sys 0m0.250s </code></pre> <p><strong>Python with PyPy:</strong></p> <pre class="lang-none prettyprint-override"><code>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 </code></pre> <p><strong>Erlang:</strong></p> <pre class="lang-none prettyprint-override"><code>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&gt; 842161320 real 0m48.259s user 0m48.070s sys 0m0.020s </code></pre> <p><strong>Haskell:</strong></p> <pre class="lang-none prettyprint-override"><code>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 </code></pre> <p><strong>Summary:</strong></p> <ul> <li>C: 100%</li> <li>Python: 692% (118% with PyPy)</li> <li>Erlang: 436% (135% thanks to RichardC)</li> <li>Haskell: 1421%</li> </ul> <p>I suppose that C has a big advantage as it uses long for the calculations and not arbitrary length integers as the other three. Also it doesn't need to load a runtime first (Do the others?).</p> <p><strong>Question 1:</strong> Do Erlang, Python and Haskell lose speed due to using arbitrary length integers or don't they as long as the values are less than <code>MAXINT</code>?</p> <p><strong>Question 2:</strong> Why is Haskell so slow? Is there a compiler flag that turns off the brakes or is it my implementation? (The latter is quite probable as Haskell is a book with seven seals to me.)</p> <p><strong>Question 3:</strong> Can you offer me some hints how to optimize these implementations without changing the way I determine the factors? Optimization in any way: nicer, faster, more "native" to the language.</p> <p><strong>EDIT:</strong></p> <p><strong>Question 4:</strong> Do my functional implementations permit LCO (last call optimization, a.k.a tail recursion elimination) and hence avoid adding unnecessary frames onto the call stack?</p> <p>I really tried to implement the same algorithm as similar as possible in the four languages, although I have to admit that my Haskell and Erlang knowledge is very limited.</p> <hr> <p>Source codes used:</p> <pre class="lang-c prettyprint-override"><code>#include &lt;stdio.h&gt; #include &lt;math.h&gt; int factorCount (long n) { double square = sqrt (n); int isquare = (int) square; int count = isquare == square ? -1 : 0; long candidate; for (candidate = 1; candidate &lt;= isquare; candidate ++) if (0 == n % candidate) count += 2; return count; } int main () { long triangle = 1; int index = 1; while (factorCount (triangle) &lt; 1001) { index ++; triangle += index; } printf ("%ld\n", triangle); } </code></pre> <hr> <pre class="lang-py prettyprint-override"><code>#! /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) &lt; 1001: index += 1 triangle += index print (triangle) </code></pre> <hr> <pre class="lang-erlang prettyprint-override"><code>-module (euler12). -compile (export_all). factorCount (Number) -&gt; factorCount (Number, math:sqrt (Number), 1, 0). factorCount (_, Sqrt, Candidate, Count) when Candidate &gt; Sqrt -&gt; Count; factorCount (_, Sqrt, Candidate, Count) when Candidate == Sqrt -&gt; Count + 1; factorCount (Number, Sqrt, Candidate, Count) -&gt; case Number rem Candidate of 0 -&gt; factorCount (Number, Sqrt, Candidate + 1, Count + 2); _ -&gt; factorCount (Number, Sqrt, Candidate + 1, Count) end. nextTriangle (Index, Triangle) -&gt; Count = factorCount (Triangle), if Count &gt; 1000 -&gt; Triangle; true -&gt; nextTriangle (Index + 1, Triangle + Index + 1) end. solve () -&gt; io:format ("~p~n", [nextTriangle (1, 1) ] ), halt (0). </code></pre> <hr> <pre class="lang-hs prettyprint-override"><code>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 &gt; 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 &gt; 1000 = triangle | otherwise = nextTriangle (index + 1) (triangle + index + 1) main = print $ nextTriangle 1 1 </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload