用级数进行对数计算,基于ln(x)求loga(b)

泰勒展开式

ln(x+1) =x - x ^ 2 / 2 + x ^ 3 / 3 - x ^ 4 / 4 + …

(|x|<=1,x!=-1)

算法原理

ln(x+1)要求 0<x+1<=2
所以要将一般数作相应变换
ln(x)
分解 x=a*2^b
ln(x)=ln(a)+ln(2^b)=ln(a)+bln(2)
此时a满足 0<a<=2 可以使用级数计算

收敛较慢,可能要循环几百次

代码实现

	static final double ln2 =       0.6931471805599453;
	static final double MINVALUE =  0.0000000000000001;
	public static double lnTaylor(double x) { 
		if(x<=0) {
			return Double.NaN;
		}
		int pow=0;
		while(x >= 1d) {
			x /= 2;
			pow++;
		}
		while(x <= 0.5d) {
			x *= 2;
			pow--;
		}
		if(Math.abs(x-1)<=MINVALUE) {
			return pow*ln2;
		}
		x=x-1;
		double y=1;
		int sign=1;
		double sum=0;
		for(int i=1;Math.abs(y)>MINVALUE;i++) {
			y=y*x;
			sum +=sign*y/i;
			sign=-sign;
		}
		return  pow*ln2+sum;
	}

快速收敛级数

|x|<=1,x!=-1, y = (x - 1) / (x + 1)

ln(x)= ln((1+y)/(1-y))

=2 y( 1 + y ^ 2 / 3 + y ^ 4 / 5 + y ^ 6 / 7 + …)

收敛较快,也就几十次。

代码实现

	public static double lnTaylor2(double x) { 
		if(x<=0) {
			return Double.NaN;
		}
		x=(x-1)/(x+1);
		double x2=x*x;
		double y=1;
		double sum=1;
		for(int i=3;y>MINVALUE;i+=2) {
			y*=x2;
			sum+=y/i;
		}
		return  2*x*sum;
	}

测试

		double x=10000;
		System.out.println("value \t"+x);
		double x1 = Math.log(x);
		System.out.println("math\t"+x1);
		
		double x2 =lnTaylor(x);
		System.out.println("Taylor\t"+x2);
		System.out.println( "dif1 \t"+(x1-x2));
		
		double x3 =lnTaylor2(x);
		System.out.println("Taylor2\t"+x3);
		System.out.println( "dif2 \t"+(x1-x3));

输出

value 10000.0
math 9.210340371976184
Taylor 9.210340371976182
dif1 1.7763568394002505E-15
Taylor2 9.210340371974409
dif2 1.7745804825608502E-12

可见精度是相当高的,误差主要来自浮点运算。

使用对数换低公式求一般对数

loga(b)=ln(b)/ ln(a)
	public static double logab(double a,double b) {
		//使用快速收敛级数提高效率
		return lnTaylor2(b)/lnTaylor2(a);
	}

测试

		System.out.println("log13(17)");
		System.out.println("log \t"+Math.log(17)/Math.log(13));
		System.out.println("log \t"+logab(13,17));

输出

log13(17)
log 1.1045884145097404
log 1.1045884145097404

猜你喜欢

转载自blog.csdn.net/qq_39464369/article/details/89818746