我从问题#12 Project
Euler
作为编程练习,并比较我在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%(使用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代码在上次调用优化方面是正确的。

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

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

  1. 埃氏筛优化(速度堪比欧拉筛) + 洛谷 P3383 线性筛素数 题解

    我们一般写的埃氏筛消耗的时间都是欧拉筛的三倍,但是欧拉筛并不好想(对于我这种蒟蒻) 虽然 -- 我 -- 也可以背过模板,但是写个不会的欧拉筛不如写个简单易懂的埃氏筛 于是就有了优化 这个优化还是比较 ...

  2. POJ 2773 Happy 2006【GCD/欧拉函数】

    根据欧几里德算法,gcd(a,b)=gcd(a+b*t,b) 如果a和b互质,则a+b*t和b也互质,即与a互质的数对a取模具有周期性. 所以只要求出小于n且与n互质的元素即可. #include&l ...

  3. BZOJ2818 欧拉函数

    题意:求1--n中满足gcd(x,y)的值为质数的数对(x,y)的数目 ( (x,y)和(y,x)算两个 ) sol: 设p[i]是一个质数,那么以下两个命题是等价的: 1.gcd(x,y)=1 2. ...

  4. uoj#38. 【清华集训2014】奇数国【欧拉函数】

     number⋅x+product⋅y=1  有整数x,y解的条件是gcd(number, product) == 1. product用线段树维护一下,然后现学了个欧拉函数. 可以这样假如x = p ...

  5. 【Unity编程】Unity中的欧拉旋转

    欧拉角的定义 在写这篇博客之前,我搜索了网上很多关于欧拉角的定义,发现大部分引用自维基百科的定义,我这里也引述一下: 维基百科定义 莱昂哈德·欧拉用欧拉角来描述刚体在三维欧几里得空间的取向.对于任何参 ...

  6. 欧拉筛法模板and 洛谷 P3383 【模板】线性筛素数(包括清北的一些方法)

    题目描述 如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) 输入格式 第一行包含两个正整数N.M,分别表示查询的范围和查询的个数. 接下来M行每行包含一个不小于1 ...

  7. 小a与黄金街道 (欧拉函数)

    题意:a, b两个人在长度为n的一维数轴上(从1开始).a在1,b在n.每个人以1m/s的速度相向而行,则每一时刻存在坐标x,y,当cgd(n, x)==1,gcd(n, y)==1时,t1=k^x, ...

  8. 欧拉筛,线性筛,洛谷P2158仪仗队

    题目 首先我们先把题目分析一下. emmmm,这应该是一个找规律,应该可以打表,然后我们再分析一下图片,发现如果这个点可以被看到,那它的横坐标和纵坐标应该互质,而互质的条件就是它的横坐标和纵坐标的最大 ...

  9. JZYZOJ1524 [haoi2012]外星人 欧拉函数

    http://172.20.6.3/Problem_Show.asp?id=1524 大概可以算一个结论吧,欧拉函数在迭代的时候,每次迭代之后消去一个2,每个非2的质因子迭代一次又(相当于)生成一个2 ...

随机推荐

  1. 基于libcurl的GET与POST(HTTP1.1)

    #include <stdio.h> #include <curl/curl.h> bool getUrl(char *filename) { CURL *curl; CURL ...

  2. empty和is_null以及isset函数在0、”0”、‘空串’、NULL、false、array()的计算值

    1empty:只要是非空或者非零的值都返回false,换句话说‘’.‘0’.0.null.false都返回true: 2is_null: 当参数满足下面三种情况时,is_null()将返回TRUE,其 ...

  3. php抓取网站图片源码

    <?php /*完成网页内容捕获功能*/ function get_img_url($site_name){     $site_fd = fopen($site_name, "r&q ...

  4. mysql增删查改

    <?php     $db_host   = 'localhost:3306';     $db_user   = 'root';     $db_passwd = '';     $db_na ...

  5. h3c 瘦ap无法上线解决办法(WA4320i-ACN)

    瘦ap无法上线的原因主要有两个:1.无法获取IP地址 2 .版本 胖ap转瘦ap: 1.使用网线+web对ap进行管理,默认IP地址为:192.168.0.50,用户名:admin 密码:h3capa ...

  6. 循环队列--忘记分配空间和如何用tag判断队空队满

    #include<iostream> #define maxsize 100 using namespace std; struct CLqueue { int *Q; int front ...

  7. win10环境下pyinstaller打包pytorch遇到的问题及解决方案

    pytorch-python源码生成windows的应用程序(.exe),报错OSError: could not get source code Failed to execute script h ...

  8. 0x10 - PostgreSQL 安装之 CentOS7 + Patroni

    PostgreSQL + CentOS7 + Patroni 背景 PostgreSQL 的高可用环境 环境 CentOS 7 pg01 (192.168.1.120) pg02 (192.168.1 ...

  9. Iterator接口(迭代器)的使用

    Iterator接口(迭代器) 前言 在程序开发中,经常需要遍历集合中的所有元素.针对这种需求,JDK专门提供了一个接口java.util.Iterator.Iterator接口也是Java集合中的一 ...

  10. 【转】修改Ubuntu系统的登陆信息的简单方法

    转自http://www.jb51.net/os/Ubuntu/414663.html Ubuntu的登陆和欢迎信息控制/etc/issue和/etc/motd/etc/issue与/etc/motd ...