Tôi đã lấy Bài toán số 12 từ Project Euler làm bài tập lập trình và để so sánh các triển khai (chắc chắn không tối ưu) của tôi trong C, Python, Erlang và Haskell. Để có được thời gian thực hiện cao hơn, tôi tìm kiếm số tam giác đầu tiên với hơn 1000 ước thay vì 500 như đã nêu trong bài toán ban đầu.
Kết quả là như sau:
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
Con trăn
lorenzo@enzo:~/erlang$ time ./euler12.py
842161320
real 1m16.632s
user 1m16.370s
sys 0m0.250s
Python với 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
Tóm lược:
- C: 100%
- Python: 692% (118% với PyPy)
- Erlang: 436% (135% nhờ RichardC)
- Haskell: 1421%
Tôi cho rằng C có lợi thế lớn vì nó sử dụng dài cho các phép tính và không phải là số nguyên độ dài tùy ý như ba số còn lại. Ngoài ra, nó không cần tải thời gian chạy trước (Làm những cái khác?).
Câu hỏi 1:
Erlang, Python và Haskell có bị mất tốc độ do sử dụng các số nguyên độ dài tùy ý hay không miễn là các giá trị nhỏ hơn MAXINT
?
Câu 2: Tại sao Haskell lại chậm như vậy? Có một cờ biên dịch tắt phanh hay là do tôi thực hiện? (Cuốn thứ hai khá có thể xảy ra vì Haskell là một cuốn sách có bảy phong ấn đối với tôi.)
Câu hỏi 3: Bạn có thể cung cấp cho tôi một số gợi ý về cách tối ưu hóa các triển khai này mà không thay đổi cách tôi xác định các yếu tố không? Tối ưu hóa theo bất kỳ cách nào: đẹp hơn, nhanh hơn, "bản địa" hơn với ngôn ngữ.
BIÊN TẬP:
Câu hỏi 4: Việc triển khai chức năng của tôi có cho phép LCO (tối ưu hóa cuộc gọi cuối cùng hay còn gọi là loại bỏ đệ quy đuôi) và do đó tránh thêm các khung không cần thiết vào ngăn xếp cuộc gọi không?
Tôi thực sự đã cố gắng thực hiện cùng một thuật toán giống nhau nhất có thể trong bốn ngôn ngữ, mặc dù tôi phải thừa nhận rằng kiến thức Haskell và Erlang của tôi rất hạn chế.
Mã nguồn được sử dụng:
#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]
. tiếng hoan hô!