两种计算自然对数的算法比较

引言

前一段时间,我写了两篇计算自然对数的算法的随笔,分别使用椭圆θ函数-算术几何平均法和泰勒级数展开式来计算。那么这两种算法的性能如何呢?在参考资料[3]中有以下说法:

 

上面的 elliptic method 就是椭圆θ函数-算术几何平均法,Taylor‘s method 2 就是我使用的泰勒级数展开式。可以看出,elliptic method 在计算精度大时占绝对优势,但在计算精度小时并不占优。而在我们的应用中,要计算的 decimal 数据类型的精度只有 28 位有效数字,即上面的 P = 28。看来在我们的应用中应该是 Taylor‘s method 2 占优。

测试程序

那么就让我们写一个 C# 程序来测试一下吧,下面就是 LogFactorialTest.cs:

 1 using System;
 2 using System.Diagnostics;
 3 using Skyiv.Extensions;
 4
 5 namespace Skyiv.Test
 6 {
 7   sealed class LogFactorialTest
 8   {
 9     static void Main(string[] args)
10     {
11       var n = (args.Length > 0) ? int.Parse(args[0]) : 10;
12       Run(2);
13       Run(n);
14     }
15
16     static void Run(int n)
17     {
18       var sw = Stopwatch.StartNew();
19       var v = LogFactorial(n);
20       sw.Stop();
21       Console.WriteLine("{0} {1,-30}: ln({2:N0}!)", sw.Elapsed, v, n);
22     }
23
24     static decimal LogFactorial(int n)
25     {
26       var v = 0m;
27       for (var i = 1; i <= n; i++) v += ((decimal)i).Log();
28       return v;
29     }
30   }
31 }

这个程序通过计算 ln(n!) 来测试两种计算自然对数的算法的性能。简要说明如下:

  1. 第 11 行取得用户在命令行参数指定的 n 值(如未指定,则使用默认值 10),用以计算 ln(n!)。
  2. 第 12 行先计算一次 ln(2!) 作热身,以便 CLR 的 JIT 先将要运行的算法代码编译为机器码。另外这两种计算自然对数的算法计算出来的 ln(2) 的值略有不同,也可以作为一个区分的标志。
  3. 第 24 至 29 行的 LogFactorial 方法就是用来计算 ln(n!) 的。它利用 ln(n!) = ln(1) + ln(2) + ... + ln(n) 来计算。
  4. 本程序使用 Stopwatch 类计时。

Linux 操作系统下编译和运行

在 Arch Linux 64-bit 操作系统的 mono 2.10.8 环境下编译和运行,结果如下所示。其中 DecimalExtensions1.cs 是参考资料[1]中的程序,DecimalExtensions2.cs 是参考资料[2]中的程序,LogFactorialTest.cs 就是上一小节的程序。

work$ dmcs --version
Mono C# compiler version 2.10.8.0
work$ dmcs LogFactorialTest.cs DecimalExtensions1.cs -out:LogFactorialTest1.exe
work$ dmcs LogFactorialTest.cs DecimalExtensions2.cs -out:LogFactorialTest2.exe
work$ mono LogFactorialTest1.exe 10000000
00:00:00.0022244 0.6931471805599453094172321215: ln(2!)
00:01:44.7091158 151180965.48756956489842537225: ln(10,000,000!)
work$ mono LogFactorialTest2.exe 10000000
00:00:00.0181903 0.6931471805599453094172321225: ln(2!)
00:03:54.9390478 151180965.48756956489842537227: ln(10,000,000!)
work$ mono LogFactorialTest1.exe 100000000
00:00:00.0022159 0.6931471805599453094172321215: ln(2!)
00:17:57.6529645 1742068084.5245154532285821925: ln(100,000,000!)
work$ mono LogFactorialTest2.exe 100000000
00:00:00.0133964 0.6931471805599453094172321225: ln(2!)
00:39:23.8652797 1742068084.5245154532285821925: ln(100,000,000!)
work$ mono LogFactorialTest1.exe 1000000000
00:00:00.0011895 0.6931471805599453094172321215: ln(2!)
03:04:05.5218954 19723265848.226982607923141006: ln(1,000,000,000!)
work$ mono LogFactorialTest2.exe 1000000000
00:00:00.0018197 0.6931471805599453094172321225: ln(2!)
05:27:32.3909935 19723265848.226982607923141007: ln(1,000,000,000!)

我们的测试程序分别使用这两种算法计算了 ln(107!)、ln(108!) 和 ln(109!) 的值,计算时间最长的将近五个半小时。

Windows 7 操作系统下编译和运行

在 Windows 7 SP1 32-bit 操作系统的 .NET Framework 4.5 环境下编译和运行:

D:\work> csc -out:LogFactorialTest1.exe LogFactorialTest.cs DecimalExtensions1.cs
Microsoft(R) Visual C# 编译器版本 4.0.30319.17929
用于 Microsoft(R) .NET Framework 4.5
版权所有 (C) Microsoft Corporation。保留所有权利。
D:\work> csc -out:LogFactorialTest2.exe LogFactorialTest.cs DecimalExtensions2.cs
Microsoft(R) Visual C# 编译器版本 4.0.30319.17929
用于 Microsoft(R) .NET Framework 4.5
版权所有 (C) Microsoft Corporation。保留所有权利。
D:\work> LogFactorialTest1 10000000
00:00:00.0034542 0.6931471805599453094172321215: ln(2!)
00:01:00.1048788 151180965.48756956489842537224: ln(10,000,000!)
D:\work> LogFactorialTest2 10000000
00:00:00.0043189 0.6931471805599453094172321214: ln(2!)
00:01:47.1634292 151180965.48756956489842537225: ln(10,000,000!)
D:\work> LogFactorialTest1 100000000
00:00:00.0034569 0.6931471805599453094172321215: ln(2!)
00:09:21.1743684 1742068084.5245154532285821925: ln(100,000,000!)
D:\work> LogFactorialTest2 100000000
00:00:00.0045334 0.6931471805599453094172321214: ln(2!)
00:18:13.4201181 1742068084.5245154532285821924: ln(100,000,000!)
D:\work> LogFactorialTest1 1000000000
00:00:00.0035446 0.6931471805599453094172321215: ln(2!)
01:36:06.8523762 19723265848.226982607923141006: ln(1,000,000,000!)
D:\work> LogFactorialTest2 1000000000
00:00:00.0043396 0.6931471805599453094172321214: ln(2!)
03:05:45.9748574 19723265848.226982607923141006: ln(1,000,000,000!)

可以看出,同样的程序,在这台机器上运行速度更快。这两台机器的型号是一样的,但这台机器 CPU 的主频比前一台稍高。

运行结果分析

上面两个运行结果整理如下表所示:

可见,在我们的应用中,算法2(椭圆θ函数-算术几何平均法)比算法1(泰勒级数展开式)大约要慢一倍左右。

运行环境

第一台机器是 2010-10-13 出厂的 Lenovo ThinkCentre M6100t PC 机,软件和硬件的信息如下所示:

第二台机器的型号和第一台相同,但出厂期日稍迟,是 2011-12-02 出厂的。相应的信息如下所示:

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-10-23 21:59:25

两种计算自然对数的算法比较的相关文章

两种计算自然对数的算法比較

引言 前一段时间.我写了两篇计算自然对数的算法的随笔,分别使用椭圆θ函数-算术几何平均法和泰勒级数展开式来计算. 那么这两种算法的性能怎样呢?在參考资料[3]中有下面说法:   上面的 elliptic method 就是椭圆θ函数-算术几何平均法.Taylor's method 2 就是我使用的泰勒级数展开式.能够看出,elliptic method 在计算精度大时占绝对优势.但在计算精度小时并不占优.而在我们的应用中,要计算的 decimal 数据类型的精度仅仅有 28 位有效数字,即上面的

集合相似度对比的两种计算算法

相似度对比的两种计算算法:Jaccard similarity相似性系数和Ochiai coefficient落和系数 Jaccard coefficient:A,B分别代表符合某种条件的集合:两个集合交集的大小/两个集合并集的大小,交集=并集意味着2个集合完全重合. Ochiai coefficient:A,B分别代表符合某种条件的集合:两个集合的交集大小/两个集合大小的几何平均值.是余弦相似性的一种形式. 相关参考链接:http://en.wikipedia.org/wiki/Jaccard

两种改进的模拟退火算法求解大值域约束满足问题2.0

2    两种改进的模拟退火算法 模拟退火算法(Simulatedannealing algorithm)是一种通用的概率算法,其思想源于固体退火过程:当固体物质温度很高时,固体内部粒子运动杂乱无序:而当温度逐渐降低时粒子又渐渐趋于有序运动.模拟退火算法往往用来求解优化问题的最小值问题,算法过程中会不断地对变量的当前赋值进行扰动,以产生新的赋值.如果新的赋值使得目标函数值变小,则接受新的赋值为当前赋值.反之,则以概率接受新的赋值,其中T是当前温度,为新赋值目标函数值,为当前赋值目标函数值,重复上

两种改进的模拟退火算法求解大值域约束满足问题1.0

0引言 约束满足问题(Constraint Satisfaction Problem,CSP)是人工智能研究领域中一个非常重要的分支,现已成为理论计算机科学.数学和统计物理学等交叉学科研究中的热点问题.人工智能.计算机科学和自动控制等领域中的许多问题都可以归结为约束满足问题.同时,约束满足问题在实际问题如模式识别.决策支持.物流调度及资源分配等领域也有着非常广泛的应用. CSP由一个变量集合和一个约束集合组成.每个变量都有一个非空的可能值域,每个约束描述了一个变量子集与子集内各变量的相容赋值,所

两种常用的全排列算法(java)

问题:给出一个字符串,输出所有可能的排列. 全排列有多种算法,此处仅介绍常用的两种:字典序法和递归法. 1.字典序法: 如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"."52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0.2都不行,5可以,将5和2交换得到"956

聊聊JVM(三)两种计算Java对象大小的方法

这篇说说如何计算Java对象大小的方法.之前在聊聊高并发(四)Java对象的表示模型和运行时内存表示 这篇中已经说了Java对象的内存表示模型是Oop-Klass模型. 普通对象的结构如下,按64位机器的长度计算 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节 3. 数据区 4.Padding(内存对齐),按照8的倍数对齐 数组对象结构是 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对

OpenGL中两种计算投影矩阵的函数

OpenGL无意间同时看到两种创建投影矩阵的写法,可以说它们完成的是同样的功能,但写法完全不同,可以观摩一下什么叫做异曲同工之妙... 第一种: gltMakeShadowMatrix函数是重点 1 // Gets the three coefficients of a plane equation given three points on the plane. 2 void gltGetPlaneEquation(GLTVector3 vPoint1, GLTVector3 vPoint2,

js实现两种实用的排序算法——冒泡、快速排序

零:数据准备,给定数组arr=[2,5,4,1,7,3,8,6,9,0]; 一:冒牌排序 1思想:冒泡排序思想:每一次对比相邻两个数据的大小,小的排在前面,如果前面的数据比后面的大就交换这两个数的位置       要实现上述规则需要用到两层for循环,外层从第一个数到倒数第二个数,内层从外层的后面一个数到最后一个数 2特点:排序算法的基础.简单实用易于理解,缺点是比较次数多,效率较低. 3实现: var times=0; var bubbleSort=function(arr){ for(var

C语言求最大公约数(两种最简单的算法实现)

第一种:用较小数的最大约数于较大数作求模元算 #include<stdio.h> /*     求两个数的最大公约数 */ int main(){     int a,b,max,min,i,result;     scanf("%d,%d",&a,&b);     printf("您输入的的值分别为%d,%d\n",a,b);          if(a>b){         max=a;         min=b;