计算自然对数的快速算法

计算自然对数的快速算法引言在 1982 年 Tateaki Sasaki 和 YasumasaKana 发表了一篇论文 PracticallyF PrecisionEva x 在这篇只有四页的论文中 他们介绍了一个计算自然对数的快速算法 实现该算法的 C 程序我们知道 NETFramework 中的 System

引言

在1982年,Tateaki. Sasaki 和 Yasumasa Kanada 发表了一篇论文:Practically Fast Multiple-Precision Evaluation of LOG(x)。在这篇只有四页的论文中,他们介绍了一个计算自然对数的快速算法。

实现该算法的 C# 程序

我们知道,.NET Framework Class Library 中的 System.Math.Log 方法只能对 dobule 数据类型计算自然对数。现在我们为 decimal 数据类型实现一个 Log 扩展方法,如下所示:

 1 using System;  2  3 namespace Skyiv.Extensions  4 {  5 static class DecimalExtensions  6  {  7 static readonly decimal pi2 = 6.m;  8 static readonly decimal ln10 = 2.m;  9 static readonly decimal eps2 = 0.00000000000001m; // 1e-14 10 static readonly decimal eps1 = eps2 * eps2; // 1e-28 11 12 public static decimal Sqrt(this decimal x) 13  { 14 if (x < 0) throw new ArgumentException("Must be nonnegative"); 15 if (x == 0) return 0; 16 var y = (decimal)Math.Sqrt((double)x); 17 return (y + x / y) / 2; 18  } 19 20 public static decimal Log10(this decimal x) 21  { 22 return Log(x) / ln10; 23  } 24 25 public static decimal Log(this decimal x) 26  { 27 if (x <= 0) throw new ArgumentException("Must be positive"); 28 if (x == 1) return 0; 29 var k = 0; 30 for (; x > 0.1m; k++) x /= 10; 31 for (; x <= 0.01m; k--) x *= 10; 32 return k * ln10 - NegativeLog(x); 33  } 34 35 static decimal NegativeLog(decimal q) 36 { // q in ( 0.01, 0.1 ] 37 decimal r = q, s = q, n = q, q2 = q * q, q1 = q2 * q; 38 for (var p = true; (n *= q1) > eps1; s += n, q1 *= q2) 39 r += (p = !p) ? n : -n; 40 decimal u = 1 - 2 * r, v = 1 + 2 * s, t = u / v; 41 decimal a = 1, b = Sqrt(1 - t * t * t * t); 42 for (; a - b > eps2; b = Sqrt(a * b), a = t) t = (a + b) / 2; 43 return pi2 / (a + b) / v / v; 44  } 45  } 46 }

在上述程序中:

  • 第 12 至 18 行实现 Sqrt 扩展方法。使用牛顿迭代法,仅迭代一次 (第 17 行)。这是因为第 16 行使用 Math.Sqrt (double) 方法给出的初值的精度已经达到 15,而 decimal 的精度为 28,迭代一足够了。
  • 第 25 至 33 行实现 Log 扩展方法。该方法先将参数 x 变换到 ( 0.01, 0.1 ] 区间中 ( 第 29 至 31 行),然后调用 NegativeLog 方法进行计算。
  • 第 35 至 44 行的 NegativeLog 方法就是我们算法的核心,使用 椭圆θ函数 ( 第 37 至 40 行 ) 和 算术几何平均法 ( 第 41 至 43 行 ) 来快速计算自然对数。该算法的原理请参阅参考资料[1]。
  • 第 7 行是事先计算出来的 2π 的值,在第 43 行中使用。
  • 第 8 行是事先计算出来的 ln10 的值,在第 32 行和 22 行中使用。
  • 我们的程序中使用 ln10 的值,而参考资料[1]中使用 ln2 的值。这是因为 decimal 使用十进制比较好。而一般的 double 使用二进制比较好。
  • 第 10 行和第 9 行的 eps1 = 10-28 和 eps2 = 10-14 使用十进制控制计算精度。而参考资料[1]中使用二进制控制计算精度,即 2-p 和 2-p/2

算法的性能

在上述程序中加入一些调试语句,就可以在程序运行时报告一些重要变量的值,结果如下所示:

q : 0.00000000000000000 k: 2 r : 0.001000 s : 0.000000 u : 0.000000 v : 1.000000 loop1: 4 b0: 0.097041 a : 0. b : 0. loop2: 3 NL: 2. Ln: 2. q : 0.00000000000000001 k: 28 r : 0.000000 s : 0.000000 u : 0. v : 1.000000 loop1: 2 b0: 0. a : 0. b : 0. loop2: 4 NL: 4. Ln: 59.77833 

上面是对 10 和 0000000000000001 分别调用 Log 方法计算自然对数的调试结果:

  • k 是 Log 方法执行到第 32 行时的值。其他变量都是第 35 至 44 行中 NegativeLog 方法执行时的值。
  • loop1 是第 38 至 39 行的 for 循环的执行次数。
  • loop2 是第 42 行的 for 循环执行次数。
  • q, r, s, u, v, a, b 都是计算终了时的值。
  • b0 是 b 的初值 (第 41 行)。a 的初值是 1 。
  • NL 是 NegativeLog 方法的返回值,即参数 q 的自然对数的相反数。
  • Ln 是 Log 方法的返回值,即参数 x 的自然对数。
  • 从上述调试结果可以看出,这个算法是非常高效的。算法中的两个 for 循环执行次数一般都不超过 4。

测试程序

下面是调用 decimal 数据类型的 Sqrt、Log 和 Log10 扩展方法的测试程序:

 1 using System;  2 using Skyiv.Extensions;  3  4 class Tester  5 {  6 static void Main()  7  {  8 foreach (var x in new decimal[] { 4 / decimal.MaxValue,  9 0.0000001m, 0.1m, 1, 10, , decimal.MaxValue }) 10  { 11 Console.WriteLine("x : " + x); 12 Console.WriteLine("sqrt: " + x.Sqrt()); 13 Console.WriteLine("ln : " + x.Log()); 14 Console.WriteLine("lg : " + x.Log10()); 15  Console.WriteLine(); 16  } 17  } 18 }

运行结果如下所示:

work$ dmcs Tester.cs DecimalExtensions.cs work$ mono Tester.exe x : 0.0000000000000000000000000001 sqrt: 0.00000000000001 ln : -64.60731 lg : -28.000000000000000000000000000 x : 0.0000001 sqrt: 0.000894 ln : -16.40182 lg : -6. x : 0.1 sqrt: 0. ln : -2. lg : -0. x : 1 sqrt: 1 ln : 0 lg : 0 x : 10 sqrt: 3. ln : 2. lg : 1.0000000000000000000000000001 x :  sqrt: 10000 ln : 18.31638 lg : 8.000000000000000000000000000 x :  sqrt: 0656.00000000000000 ln : 66.83660 lg : 28.33893 

从中可以看出,这个算法的运算精度能够达到 27,只有最后一位可能有误差。

参考资料

  1. CiNii Articles: Practically Fast Multiple-Precision Evaluation of LOG(X)
  2. Wikipedia: Natural logarithm
  3. Wikipedia: Arithmetic-geometric mean
  4. Wikipedia: Newton’s method
  5. MSDN: Decimal 结构 (System)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/207723.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午1:14
下一篇 2026年3月19日 下午1:15


相关推荐

  • SpringCloud与Dubbo的比较

    SpringCloud与Dubbo的比较Dubbo一、dubbo简介Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。Dubbo是一款高性能、轻量级的开源JavaRPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。现已发展成为Apache的顶级孵化开源项目,详见官网:…

    2022年6月14日
    30
  • OpenProcessToken

    OpenProcessTokenOpenProcessToken  要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安全进程执行OpenProcess(PROCESS_ALL_ACCES

    2022年6月25日
    24
  • jmeter接口测试详细教程

    jmeter接口测试详细教程jmeter 接口测试详细教程 jmeter 接口测试 总结 1 你们公司的接口测试流程是怎样的 有没有感觉熟悉 貌似在哪里听过 接口测试我们是在 XX 项目做的 主要有 XX 接口 XX 接口 XX 接口等 1 首先是从开发那里拿到 API 接口文档 了解接口业务 包括接口地址 请求方式 入参 出参 token 鉴权 返回格式等信息 2 然后使用 Postman 或 Jmeter 工具执行接口测试 一般使用 Jmeter 的步骤是这样的 1 首先新建一个线程组 2 然后就是新建一个 HTTP 请求默认值 输入接口服务器 IP 和端口

    2026年3月19日
    2
  • ES6数组常用方法总结[通俗易懂]

    ES6数组常用方法总结[通俗易懂]这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML图表FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Mar

    2022年6月3日
    47
  • 什么是CICD?

    什么是CICD?传统的应用发布模式如果你经历体验过传统的应用发布,你可能就会觉得CICD有足够吸引你的地方,反之亦然。一般一个研发体系中都会存在多个角色:开发、测试、运维。当时我们的应用发布模式可以能是…

    2022年5月24日
    51
  • LLM大模型和文心一言、豆包、deepseek对比

    LLM大模型和文心一言、豆包、deepseek对比

    2026年3月12日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号