如何实现对数ln运算?

好朋友Z说他们期末得设计一个计算器,对数ln的运算挺麻烦的。

我想,这不是就是泰勒展开的事吗?

泰勒展开1.0

立刻想到了这个嘛:


然而,跑一下程序的结果是这样的:


1.1 循环的次数多——Peano余项为o(x^n),收敛的速度慢;x越接近2,收敛就越慢。

1.2 当x>1时,增量不收敛,然后答案跑不出来了= =

因为设置的跳出循环的条件为:

       

while(fabs(delta/i)>Epsilon)

        所以这样展开将进入死循环

面对这1.1和1.2两个缺陷,我不由得觉得自己最开始想得太天真。

泰勒展开2.0

教材上的一道例题——提高ln2的求解精度,同时解释了1.0泰勒展开的缺陷:


(《数学分析(上) 第二版》陈纪修 等著 P208 )

因为Peano余项的Taylor展开是以x逼近x0为前提的,所以当x远离0的时候,Maclaurin公式的精度将会很低

受此启发,当采用换元法之后:


这样进行Talyor展开:


2.1 因为对数函数的定义域大于0,可知——不管α取定义域内何值,t始终在0~1之间,比泰勒展开1.0更逼近x0=0

2.2 始终是收敛级数

2.3 收敛速度快——Peano余项为o(t^(2n+2)),更容易增长到更高阶

高兴地跑了跑程序:

(图中的“第一种”是泰勒展开2.0,“第二种”是泰勒展开1.0


上述的几大特点都体现出来了——循环次数少、精度更高、适用范围更广,然而——

2.4 可知当α逼近+∞时,t逼近1,与x0=0距离太远,精度降低(图中对于10000连0.1精度都没达到)


泰勒展开3.0

目前的问题,集中在大数的求对数上了。

最开始想类比换元法,设计一个定义域控制更加严格的t的换元法,无果。

后来觉得——大数应该拆成小数!

举个例子: ln(123456)=ln(1.23456*10^5)=ln(1.23456)+5ln10


对于α小于10,用泰勒展开2.0,当α>10就视为大数,拆为k倍ln10和lnN (0<N<10)之和,lnN精度用泰勒展开2.0保证在10e-6,而ln10的值事先存储:

#define ln10 2.30258509299404568401

然后跑一跑程序:

(“第一种”为泰勒展开3.0,“第二种”为泰勒展开2.0,,“第三种”为泰勒展开1.0



泰勒展开3.0的优点是:

3.1 大数运算精确

3.2 循环次数少,收敛速度快 (0-20次,图中后两次有误,无碍)

3.3 不远离x0=0,精度高


缺点有:

3.4 对于ln0.1这样的小数运算精度反倒不如泰勒展开1.0

3.5 由于有两个lnN,不能明确确定精度范围

优化思路大抵如上,但剩下的这些缺点如何解决?实际上ln的运算是如何实现的呢?我们可以越挖越深......感谢Z提供了一个如此有思考价值的问题~吐舌头


附C语言代码如下:

#include "stdafx.h"
#include <math.h>
#define Epsilon 10e-6
#define ln10 2.30258509299404568401

long double ln(long double c)
{long double s2=0.0,delta;int i=1;
if(c==1) 	{printf("循环次数为%d\n",i-1);return 0;}
c=(c-1.0)/(c+1.0);//Marclurin公式变换技巧
	delta=c;i=0;
	do{ s2+=delta/(2*i+1);
		delta*=c*c;i++;
}while(fabs((2*delta)/(2*i+1))>Epsilon);
	printf("循环次数为%d\n",i-1);
return 2*s2;
}

int main()
{ long double c,s1=0.0,s2=0.0,input,delta,standard;int i=1,j=1,d;
	while(1)
	{
	printf("精度为10e-6\n");
	printf("ln");
	scanf("%lf",&c);
	
	if(c==1) {printf("%.10lf\n",s2);return 0;}
	else { printf("第一种展开方式——————\n");//泰勒展开3.0
		input=c;
		for(d=0;input>=10;d++)input/=10;
			printf("结果为:%.10f\n",d*ln10+ln(input));
		
	    printf("第二种展开方式——————\n");//泰勒展开2.0
	    printf("结果为%.10f\n",ln(c));
	
		if(c>2.0) //泰勒展开1.0
		{printf("第三种展开方式——————\n当x>1时增量不收敛,第三种展开方式进入死循环\n");
		goto start;
		}
		input=c-1.0;//方便使用Maclaurin公式
		delta=input;i=1;
		do{ 
		if(i%2) s1+=delta/i;
		else s1-=delta/i;
		delta*=input;i++;
		}while(fabs(delta/i)>Epsilon);	//当x>1时增量不收敛,进入死循环
		printf("第三种展开方式——————\n");
		printf("循环次数为%d\n",i);
		printf("结果为%.10f\n",s1);
   
 start: printf("Windows计算器结果为:");
		    scanf("%lf",&standard);
		    putchar('\n');
			s1=s2=0.0;i=j=1; 
    }
	}
return 0;
}




  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值