Saya telah mengambil Soal # 12 dari Project Euler sebagai latihan pemrograman dan untuk membandingkan implementasi (pasti tidak optimal) saya dalam C, Python, Erlang dan Haskell. Untuk mendapatkan waktu eksekusi yang lebih tinggi, saya mencari nomor segitiga pertama dengan lebih dari 1000 pembagi daripada 500 seperti yang dinyatakan dalam masalah asli.
Hasilnya adalah sebagai berikut:
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 dengan 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
Ringkasan:
- C: 100%
- Python: 692% (118% dengan PyPy)
- Erlang: 436% (135% terima kasih untuk RichardC)
- Haskell: 1421%
Saya mengira bahwa C memiliki keuntungan besar karena menggunakan panjang untuk perhitungan dan bukan bilangan bulat panjang sewenang-wenang seperti tiga lainnya. Juga tidak perlu memuat runtime terlebih dahulu (Lakukan yang lain?).
Pertanyaan 1:
Apakah Erlang, Python dan Haskell kehilangan kecepatan karena menggunakan bilangan bulat panjang sewenang-wenang atau tidak mereka asalkan nilainya kurang dari MAXINT
?
Pertanyaan 2: Mengapa Haskell begitu lambat? Apakah ada flag compiler yang mematikan rem atau implementasi saya? (Yang terakhir sangat mungkin karena Haskell adalah buku dengan tujuh meterai bagi saya.)
Pertanyaan 3: Dapatkah Anda menawarkan saya beberapa petunjuk bagaimana mengoptimalkan implementasi ini tanpa mengubah cara saya menentukan faktor-faktornya? Optimalisasi dengan cara apa pun: lebih baik, lebih cepat, lebih "asli" ke bahasa.
EDIT:
Pertanyaan 4: Apakah implementasi fungsional saya memungkinkan LCO (optimasi panggilan terakhir, alias penghapusan rekursi ekor) dan karenanya menghindari menambahkan frame yang tidak perlu ke tumpukan panggilan?
Saya benar-benar mencoba menerapkan algoritma yang sama dengan semirip mungkin dalam empat bahasa, meskipun saya harus mengakui bahwa pengetahuan Haskell dan Erlang saya sangat terbatas.
Kode sumber yang digunakan:
#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]
. Hore!