与项目欧拉速度比较:C vs Python与Erlang vs Haskell

我从问题#12 Project
Euler作为编程练习,并比较我在C,Python,Erlang和Haskell中的实现(当然不是最优)实现。为了获得更高的执行时间,我搜索了第一个有1000个以上因子的三角形数字,而不是原始问题中所述的500个。

结果如下:

<强> C:

[email protected]:~/erlang$ gcc -lm -o euler12.bin euler12.c
[email protected]:~/erlang$ time ./euler12.bin
842161320

real    0m11.074s
user    0m11.070s
sys 0m0.000s

<强>的Python:

[email protected]:~/erlang$ time ./euler12.py
842161320

real    1m16.632s
user    1m16.370s
sys 0m0.250s

Python与PyPy:**

[email protected]:~/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

<强>二郎:

[email protected]:~/erlang$ erlc euler12.erl
[email protected]:~/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中:

[email protected]:~/erlang$ ghc euler12.hs -o euler12.hsx
[1 of 1] Compiling Main             ( euler12.hs, euler12.o )
Linking euler12.hsx ...
[email protected]:~/erlang$ time ./euler12.hsx
842161320

real    2m37.326s
user    2m37.240s
sys 0m0.080s

<强>要点:

  • C:100%
  • Python:692%(使用PyPy的占118%)
  • Erlang:436%(135%感谢RichardC)
  • Haskell:1421%

我认为C有一个很大的优势,因为它用了很长的时间来计算,而不是任意长度的整数。另外它不需要首先加载运行时(其他人?)。

问题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 ("%ldn", 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

在x86_64 Core2 Duo(2.5GHz)机器上使用GHC 7.0.3gcc 4.4.6Linux
2.6.29
编译使用ghc -O2 -fllvm -fforce-recomp用于Haskell和gcc -O3 -lm用于C。

  • 您的C例程运行时间为8.4秒(比运行速度快,可能是因为-O3
  • Haskell解决方案在36秒内运行(由于-O2标志)
  • 您的factorCount'代码没有显式键入,并且默认为Integer(感谢Daniel纠正我的错误!使用Int和时间更改为 11.1秒
    提供显式类型签名(这是标准练习) factorCount’中,你有不必要的 fromIntegral
    `。修正后的结果没有改变(编译器很聪明,对你来说很幸运)。
  • 您使用了mod,其中rem更快更充分。这会将时间更改为 8.5秒
  • factorCount'大专栏  与项目欧拉速度比较:C vs Python与Erlang vs Haskellode>不断地应用两个额外的参数(numbersqrt)。工人/包装转换给我们:
$ time ./so
842161320  

real    0m7.954s
user    0m7.944s
sys     0m0.004s

没错, 7.95秒 。始终比C解决方案快0.5秒。**如果没有-fllvm标志,我仍然得到8.182秒,所以NCG后端在这种情况下也做得很好。

结论:Haskell真棒。

结果代码

factorCount number = factorCount' number isquare 1 0 - (fromEnum $ square == fromIntegral isquare)
    where square = sqrt $ fromIntegral number
          isquare = floor square

factorCount' :: Int -> Int -> Int -> Int -> Int
factorCount' number sqrt candidate0 count0 = go candidate0 count0
  where
  go candidate count
    | candidate > sqrt = count
    | number `rem` candidate == 0 = go (candidate + 1) (count + 2)
    | otherwise = go (candidate + 1) count

nextTriangle index triangle
    | factorCount triangle > 1000 = triangle
    | otherwise = nextTriangle (index + 1) (triangle + index + 1)

main = print $ nextTriangle 1 1

编辑:所以现在我们已经探索了,让我们解决问题

Question 1: 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 MAXINT?

在Haskell中,使用IntegerInt慢,但是慢多少取决于所执行的计算。幸运的是(对于64位机器)Int就足够了。为了可移植性,你应该重写我的代码来使用Int64或者Word64(C不是唯一带有long的语言) p>

Question 2: 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.)

>

Question 3: 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.

这就是我上面回答的问题。答案是

  • 0)通过-O2使用优化
  • 1)尽可能使用快速(特别是:取消箱式)类型
    2)rem不是mod(一个经常被遗忘的优化)和

3)工人/包装转换(也许是最常见的优化)

Question 4: Do my functional implementations permit LCO and hence avoid
adding unnecessary frames onto the call stack?

是的,这不是问题。好的工作,很高兴你考虑这个。

Erlang的实现有一些问题。作为以下的基准,我的未修改的Erlang程序的执行时间是47.6秒,而C代码是12.7秒。

如果你想运行计算密集的Erlang代码,你应该做的第一件事就是使用本地代码。用erlc + native
euler12编译
的时间缩短到了41.3秒。然而,这种代码的本地编译速度要低很多(只有15%),问题在于你使用了-compile(export_all)。这对于实验是有用的,但所有功能都可以从外部访问的事实导致本地编译器非常保守。
(正常的BEAM模拟器没有太大的影响。)用-export([solve / 0])替换这个声明。提供了更好的加速:31.5秒(几乎是基线的35%)。

但是代码本身存在一个问题:对于factorCount循环中的每次迭代_,执行此测试:

factorCount (_, Sqrt, Candidate, Count) when Candidate == Sqrt -> Count + 1;

C代码不这样做。一般来说,在相同代码的不同实现之间进行公平的比较是非常棘手的,特别是如果算法是数值的,因为你需要确定它们实际上是在做同样的事情。在一个实现中由于某种类型转换而导致的轻微舍入错误可能会导致它执行比其他更多的迭代,即使两者最终都达到相同的结果。

为了消除这个可能的错误源(并且在每次迭代中摆脱额外的测试),我重写了factorCount函数,如下所示,精确地模拟C代码:

factorCount (N) ->
    Sqrt = math:sqrt (N),
    ISqrt = trunc(Sqrt),
    if ISqrt == Sqrt -> factorCount (N, ISqrt, 1, -1);
       true          -> factorCount (N, ISqrt, 1, 0)
    end.

factorCount (_N, ISqrt, Candidate, Count) when Candidate > ISqrt -> Count;
factorCount ( N, ISqrt, Candidate, Count) ->
    case N rem Candidate of
        0 -> factorCount (N, ISqrt, Candidate + 1, Count + 2);
        _ -> factorCount (N, ISqrt, Candidate + 1, Count)
    end.

这个重写,没有export_all和本地编译,给了我下面的运行时间:

$ erlc +native euler12.erl
$ time erl -noshell -s euler12 solve
842161320

real    0m19.468s
user    0m19.450s
sys 0m0.010s

这与C代码相比并不算太坏:

$ time ./a.out
842161320

real    0m12.755s
user    0m12.730s
sys 0m0.020s

考虑到Erlang完全不适合编写数字代码,在这样的程序上只比C慢50%。

最后,关于你的问题:

问题1:由于使用了任意长度的整数或者是erlang,python和haskell,所以速度很慢 只要数值小于MAXINT,是不是他们?**

是的,有点。在Erlang中,没有办法说“使用32/64位算术进行换行”,所以除非编译器能够证明整数(通常不能),否则必须检查所有的计算才能看到如果他们可以适应一个单一的标签的单词,或者如果它必须把它们变成堆分配bignums。即使在运行时实际上没有使用过这样的元素,这些检查也必须执行。另一方面,这意味着你知道如果你突然给它比以前更大的输入,算法将永远不会失败,因为一个意外的整数回绕。

问题4:我的功能实现是否允许使用LCO,从而避免在调用堆栈中添加不必要的帧?**

是的,您的Erlang代码在上次调用优化方面是正确的。

未经作者同意,本文严禁转载,违者必究!

原文地址:https://www.cnblogs.com/lijianming180/p/12026719.html

时间: 2024-11-08 23:47:36

与项目欧拉速度比较:C vs Python与Erlang vs Haskell的相关文章

欧拉计划013(ProjectEuler013):求出10个数乘积最大的

申明:之前的所有欧拉计划都是用python来写的,的确python来写,代码量极少,学习起来也很方便.但是最近为了找java工作,所以用Java来完成欧拉计划的题,来复习一下Java. Large sum Problem 13 Work out the first ten digits of the sum of the following one-hundred 50-digit numbers. 371072875339021027987979982208375902465101357402

欧拉项目005:最小公倍数

Smallest multiple Problem 5 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? 就是找出1....20 所有数的最

欧拉项目004:寻找最大的回文数

Problem 4: Largest palindrome product A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99. Find the largest palindrome made from the product of two 3-digit numbers. 寻找有两

006:欧拉项目平方和与和的平方的差

Sum square difference Problem 6 The sum of the squares of the first ten natural numbers is, 12 + 22 + ... + 102 = 385 The square of the sum of the first ten natural numbers is, (1 + 2 + ... + 10)2 = 552 = 3025 Hence the difference between the sum of

欧拉项目007:第10001个素数

10001st prime Problem 7 By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. What is the 10 001st prime number? 使用埃拉托斯特尼筛法,不懂得自行Wiki 我的python代码: # -*- coding:utf-8 -*- #使用埃拉托斯尼特晒法 #从2开始 import math def

【转】欧拉函数

链接:http://www.cnblogs.com/yefeng1627/archive/2013/01/02/2842492.html 欧拉函数直接计算公式 欧拉函数的定义: E(N)= (  区间[1,N-1] 中与 N 互质的整数个数). 对于 积性函数 F(X*Y),当且仅当 GCD(X,Y)= 1 时, F(X*Y) = F(X)* F(Y) 任意整数可因式分解为如下形式:      其中( p1, p2 ... pk 为质数, ei 为次数 ) 所以 因为 欧拉函数 E(X)为积性函

欧拉函数o(n)求素数

欧拉函数的定义: E(N)= ( 区间[1,N-1] 中与 N 互质的整数个数). 对于 积性函数 F(X*Y),当且仅当 GCD(X,Y)= 1 时, F(X*Y) = F(X)* F(Y) 任意整数可因式分解为如下形式: 其中( p1, p2 - pk 为质数, ei 为次数 ) 所以 因为 欧拉函数 E(X)为积性函数, 所以 对于 , 我们知道 因为pi 为质数,所以 [ 1, pi-1 ] 区间的数都与 pi 互质 对于 区间[ 1, ] ,共有 个数, 因为 只有一个质因子, 所以与

BZOJ2818 欧拉函数

题意:求1--n中满足gcd(x,y)的值为质数的数对(x,y)的数目 ( (x,y)和(y,x)算两个 ) sol: 设p[i]是一个质数,那么以下两个命题是等价的: 1.gcd(x,y)=1 2.gcd(x*p[i],y*p[i])=p[i] eg:gcd(36,25)=1,gcd(36*7,25*7)=7 所以对于1--n的所有质数p[i],求一下1<=x,y<=n/p[i]中所有gcd(x,y)=1的数对的数目即可. 如何求1--r范围内所有互质数对的数目? 考虑欧拉函数φ(x)=1.

欧拉函数 / 蒙哥马利快速幂 / 容斥

一:知识点 欧拉函数参考1 浅谈欧拉函数参考2 欧拉函数的定义: 在数论中,对于正整数N,少于或等于N ([1,N]),且与N互质(即gcd为1)的正整数(包括1)的个数,记作φ(n).     欧拉函数的延伸: 小于或等于n的数中,与n互质的数的总和为:φ(x) * x / 2  (n>1). 欧拉函数φ(x)模板: ll Euler(int n)//即求φ(x) { ll ret=n; for(int i=2;i<=sqrt(n);i++) if(n%i==0) { ret=ret/i*(