【发布时间】:2011-10-21 07:40:07
【问题描述】:
我将Project Euler 中的Problem #12 作为编程练习,并比较了我在C、Python、Erlang 和Haskell 中的(肯定不是最佳的)实现。为了获得更高的执行时间,我搜索了第一个具有超过 1000 个除数的三角形数,而不是原始问题中所述的 500 个。
结果如下:
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 与 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
二郎:
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
总结:
- C:100%
- Python:692%(118% 使用 PyPy)
- Erlang:436%(135% 感谢 RichardC)
- 哈斯克尔:1421%
我认为 C 有一个很大的优势,因为它使用 long 进行计算,而不是像其他三个那样使用任意长度的整数。它也不需要先加载运行时(其他的呢?)。
问题 1:
Erlang、Python 和 Haskell 是否会因为使用任意长度的整数而降低速度,或者只要值小于MAXINT,它们就不会降低速度吗?
问题 2: 为什么 Haskell 这么慢?是否有关闭刹车的编译器标志或者是我的实现? (后者很可能是因为 Haskell 对我来说是一本有七个印章的书。)
问题 3: 你能给我一些提示,如何在不改变我确定因素的方式的情况下优化这些实现吗?以任何方式进行优化:更好、更快、更“原生”的语言。
编辑:
问题 4: 我的功能实现是否允许 LCO(最后一次调用优化,也就是尾递归消除),从而避免在调用堆栈中添加不必要的帧?
我确实尝试过在四种语言中实现尽可能相似的相同算法,尽管我不得不承认我的 Haskell 和 Erlang 知识非常有限。
使用的源代码:
#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
【问题讨论】:
-
@Jochen(和 Seth) 并不是说 C 语言很快或很棒,但它被认为很容易编写高性能代码(这可能不是真的,但大多数程序似乎都能够,所以真的足够的)。正如我在回答中所探索的那样,随着时间的推移发现这是正确的,程序员技能和所选语言的常见优化知识非常重要(尤其是对于 Haskell)。
-
刚刚用 Mathematica 检查过——需要 0.25 秒(这里用 C 需要 6 秒),代码就是:
Euler12[x_Integer] := Module[{s = 1}, For[i = 2, DivisorSigma[0, s] < x, i++, s += i]; s].万岁! -
还有其他人记得 C 和汇编之间的这些战争吗? “当然!你可以用 C 语言编写代码快 10 倍,但你的 C 代码可以运行这么快吗?...”我敢肯定机器代码和汇编之间也有同样的战斗。
-
@JS:可能不会,因为汇编只是您键入的一组助记符,而不是原始二进制机器码——通常它们之间存在 1-1 对应关系。
-
结论,对于 Haskell:-O2 使其加速大约 3 倍,而使用 Int 而不是 Integer 大约 4x-6x,总加速达到 12x-14x 甚至更多。
标签: python c performance haskell erlang