与项目欧拉速度比较:C vs Python与Erlang vs Haskell
我从问题#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.3,gcc 4.4.6,Linux编译使用
2.6.29ghc -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>不断地应用两个额外的参数(number,sqrt)。工人/包装转换给我们:
$ 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中,使用Integer比Int慢,但是慢多少取决于所执行的计算。幸运的是(对于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的时间缩短到了41.3秒。然而,这种代码的本地编译速度要低很多(只有15%),问题在于你使用了
euler12编译-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的更多相关文章
- 埃氏筛优化(速度堪比欧拉筛) + 洛谷 P3383 线性筛素数 题解
我们一般写的埃氏筛消耗的时间都是欧拉筛的三倍,但是欧拉筛并不好想(对于我这种蒟蒻) 虽然 -- 我 -- 也可以背过模板,但是写个不会的欧拉筛不如写个简单易懂的埃氏筛 于是就有了优化 这个优化还是比较 ...
- 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 ...
- BZOJ2818 欧拉函数
题意:求1--n中满足gcd(x,y)的值为质数的数对(x,y)的数目 ( (x,y)和(y,x)算两个 ) sol: 设p[i]是一个质数,那么以下两个命题是等价的: 1.gcd(x,y)=1 2. ...
- uoj#38. 【清华集训2014】奇数国【欧拉函数】
number⋅x+product⋅y=1 有整数x,y解的条件是gcd(number, product) == 1. product用线段树维护一下,然后现学了个欧拉函数. 可以这样假如x = p ...
- 【Unity编程】Unity中的欧拉旋转
欧拉角的定义 在写这篇博客之前,我搜索了网上很多关于欧拉角的定义,发现大部分引用自维基百科的定义,我这里也引述一下: 维基百科定义 莱昂哈德·欧拉用欧拉角来描述刚体在三维欧几里得空间的取向.对于任何参 ...
- 欧拉筛法模板and 洛谷 P3383 【模板】线性筛素数(包括清北的一些方法)
题目描述 如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) 输入格式 第一行包含两个正整数N.M,分别表示查询的范围和查询的个数. 接下来M行每行包含一个不小于1 ...
- 小a与黄金街道 (欧拉函数)
题意:a, b两个人在长度为n的一维数轴上(从1开始).a在1,b在n.每个人以1m/s的速度相向而行,则每一时刻存在坐标x,y,当cgd(n, x)==1,gcd(n, y)==1时,t1=k^x, ...
- 欧拉筛,线性筛,洛谷P2158仪仗队
题目 首先我们先把题目分析一下. emmmm,这应该是一个找规律,应该可以打表,然后我们再分析一下图片,发现如果这个点可以被看到,那它的横坐标和纵坐标应该互质,而互质的条件就是它的横坐标和纵坐标的最大 ...
- JZYZOJ1524 [haoi2012]外星人 欧拉函数
http://172.20.6.3/Problem_Show.asp?id=1524 大概可以算一个结论吧,欧拉函数在迭代的时候,每次迭代之后消去一个2,每个非2的质因子迭代一次又(相当于)生成一个2 ...
随机推荐
- 第37章 socket编程 之练习:实现简单的web服务器
一.参考网址 1.linux C学习之实现简单的web服务器 2.C语言实现简单Web服务器(一)
- 函数返回值return
#函数后面如果没有return系统会默认return none def ff(): print("打印return") return 15 # 函数在执行中遇到return就会停止 ...
- Java 10按钮设计(awt)
/** * 2019年8月9日08:03:41 * 目的:利用Java设计10个按钮 * @author 张涛 * */ //导入awt包 import java.awt.*; import java ...
- ZJNU 1205 - 侦探推理——高级
双层枚举嫌疑犯与当日是星期几,统计真话与假话是否满足题意 注意 fake<=N&&fake+neutral>=N 即假话数量不大于N,假话加上没用的废话数量不小于N (注意 ...
- Codeforces 1294E - Obtain a Permutation
题目大意: 给定一个n*m的矩阵 可以更改任意一个位置的值 也可以选择一整列全部往上移动一位,最上方的数移动到最下方 问最少操作多少次可以把这个矩阵移动成 1 2 3 ... m m+1 m+2 m+ ...
- 吴裕雄--天生自然 PYTHON3开发学习:输入和输出
str = input("请输入:"); print ("你输入的内容是: ", str) # 打开一个文件 f = open("/tmp/foo.t ...
- C#通过窗体应用程序操作数据库(增删改查)
为了体现面向对象的思想,我们把“增删改查”这些函数封装到一个数据库操作类里: 为了便于窗体程序与数据库之间进行数据交互,我们建一个具有数据库行数据的类,通过它方便的在窗体程序与数据库之间传输数据: 我 ...
- day54-mysql-库、表、数据操作
. 什么是数据库 存储数据的仓库 . 什么数据: 大家所知道的都是数据.比如:你同学的名字,年龄,性别等等 . 数据库概念 .数据库服务器 .数据库管理系统 重点 .库 .表 .记录 .数据 参考画图 ...
- django项目班笔记-模板抽取
目录 一.将前端静态文件放置到项目文件目录 二.模板设置 三.将静态文件拖放到项目对应目录 四.检查HTML文件中的应用是否自动更改了 4.1 文件引用没有改变的解决方法 4.2 设置static文件 ...
- 一、linux-mysql 运维DBA介绍
一.DBA数据库管理人员需要在整个架构中解决数据库的压力,前端业务通过扩展,加机器就可以很好的解决,但是存储.数据库就不是很好的可以进行扩展,数据也是分配不均的,所以,1)通过在数据库前面添加Memc ...