3.2 分数和复数
看其他篇章到目录选择。
我们讲到数学的计算,难免会遇到分数形式,因为实数的定义就是可以表示为一个分数的形式的数,而加入虚数的复数也是偶尔会遇到的。Commons Math包中的fraction和complex包就分别提供了方法来表示这两种数。
首先来看一个接口FieldElement<T>,这个接口定义了一个泛型参数,其具体内容定义了5组方法,分别是加减乘除运算和getField()方法。具体Field<T>是什么,这里就不研究了,因为我发现暂时还没有用到它。呵呵。
像这样的无算法和实现的类,具体就是看示例来观察它的运算行为了。
正式开始,先看看Fraction类,在fraction子包下的Fraction类继承了java中的Number类,同时实现了接口FieldElement<Fraction>。Fraction的构造方法有5个,支持给定一个实数来构建分数,也支持给定分子分母的构建方法。具体的见代码吧,因为代码已经非常直观了。
1
/** *//**
2
*
3
*/
4
package algorithm.math;
5
6
import org.apache.commons.math.complex.Complex;
7
import org.apache.commons.math.fraction.Fraction;
8
import org.apache.commons.math.fraction.FractionConversionException;
9
10
/** *//**
11
* @author Jia Yu
12
* @date 2010-11-29
13
*/
14
public class FractionAndComplexTest
{
15
16
/** *//**
17
* @param args
18
*/
19
public static void main(String[] args)
{
20
// TODO Auto-generated method stub
21
fraction();
22
System.out.println("------------------------------------------------");
23
complex();
24
}
25
26
private static void complex()
{
27
// TODO Auto-generated method stub
28
Complex z = new Complex(3.0, 4.0);
29
Complex x = new Complex(5.0, 7.0);
30
31
//output directly
32
System.out.println("complex z = "+z);//ugly
33
System.out.println("complex z = "+c2s(z));//organized manually
34
System.out.println("complex x = "+c2s(x));
35
36
//complex abs
37
System.out.println("abs of z = "+z.abs());
38
//complex add
39
System.out.println("z+x = "+c2s(z.add(x)));
40
//complex substrac
41
System.out.println("z-x = "+c2s(z.subtract(x)));
42
//complex multiply
43
System.out.println("z*x = "+c2s(z.multiply(x)));
44
//complex sin cos
45
System.out.println("sin(z) = "+c2s(z.sin()));
46
System.out.println("cos(z) = "+c2s(z.cos()));
47
//complex conjugate
48
System.out.println("conjugate of z = "+c2s(z.conjugate()));
49
}
50
51
public static String c2s(Complex c)
{
52
if(c.getImaginary()<0)
53
return ""+c.getReal()+c.getImaginary()+"i";
54
return ""+c.getReal()+"+"+c.getImaginary()+"i";
55
}
56
57
private static void fraction()
{
58
// TODO Auto-generated method stub
59
Fraction frac1 = new Fraction(2,3);
60
61
//output directly
62
System.out.println("frac1 = "+frac1);
63
64
try
{
65
Fraction frac2 = new Fraction(0.5);
66
System.out.println("frac2 = "+frac2);
67
68
//fraction doublevalue
69
System.out.println("frac1's double form is "+frac1.doubleValue());
70
//fraction add
71
System.out.println("frac1+frac2 = "+frac1.add(frac2));
72
//fraction substract
73
System.out.println("frac1-frac2 = "+frac1.subtract(frac2));
74
//fraction multiply
75
System.out.println("frac1*frac2 = "+frac1.multiply(frac2));
76
//fraction divide
77
System.out.println("frac1/frac2 = "+frac1.divide(frac2));
78
//fraction multiplicative inverse
79
System.out.println("frac1's inverse is "+frac1.reciprocal());
80
81
//fraction reduction
82
System.out.println("5 / 25 = "+Fraction.getReducedFraction(5,25));
83
} catch (FractionConversionException e)
{
84
// TODO Auto-generated catch block
85
e.printStackTrace();
86
}
87
}
88
89
}
90
看了具体用法,不免要探究一下,分数的这些运算在Commons Math里是怎么实现的。跟我们直观感觉一样,在Fraction类内部定义了分子和分母两个int型的成员变量。我忍不住要把构造方法的源码贴上来:
1
private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
2
throws FractionConversionException
3
{
4
long overflow = Integer.MAX_VALUE;
5
double r0 = value;
6
long a0 = (long)Math.floor(r0);
7
if (a0 > overflow)
{
8
throw new FractionConversionException(value, a0, 1l);
9
}
10
11
// check for (almost) integer arguments, which should not go
12
// to iterations.
13
if (Math.abs(a0 - value) < epsilon)
{
14
this.numerator = (int) a0;
15
this.denominator = 1;
16
return;
17
}
18
19
long p0 = 1;
20
long q0 = 0;
21
long p1 = a0;
22
long q1 = 1;
23
24
long p2 = 0;
25
long q2 = 1;
26
27
int n = 0;
28
boolean stop = false;
29
do
{
30
++n;
31
double r1 = 1.0 / (r0 - a0);
32
long a1 = (long)Math.floor(r1);
33
p2 = (a1 * p1) + p0;
34
q2 = (a1 * q1) + q0;
35
if ((p2 > overflow) || (q2 > overflow))
{
36
throw new FractionConversionException(value, p2, q2);
37
}
38
39
double convergent = (double)p2 / (double)q2;
40
if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator)
{
41
p0 = p1;
42
p1 = p2;
43
q0 = q1;
44
q1 = q2;
45
a0 = a1;
46
r0 = r1;
47
} else
{
48
stop = true;
49
}
50
} while (!stop);
51
52
if (n >= maxIterations)
{
53
throw new FractionConversionException(value, maxIterations);
54
}
55
56
if (q2 < maxDenominator)
{
57
this.numerator = (int) p2;
58
this.denominator = (int) q2;
59
} else
{
60
this.numerator = (int) p1;
61
this.denominator = (int) q1;
62
}
63
64
}
65
为什么是私有的?因为这是最根本的构造方法,其他方法都是调用了这个基本方法。具体参数的解释:value代表了要转化的double值,epsilon表示了最大误差范围,maxDenominator表示支持的最大分母,maxIterations表示最大的渐进分数。这个构造方法最后得到了分子和分母的表示(numerator和denominator)。其他的运算代码就不用贴了,因为表示出了分数的分子分母,其他的运算容易实现(其实是很有趣的,看如何通分约分,利用gcd算法)。
分数的大分数表示有BigFraction类,具体的用法可以看源码。
下面是复数的实现:
关于复数的概念,Complex类里定义了两个double变量分别表示复数的实部real和虚部imaginary。复数的运算相比分数要简单一些,直观上看毕竟是一个线性组合,所以相对的加减乘除运算不涉及到通分和约分这样需要GCD才能做到的事情。具体的实现不多讲了,没有意义,直接上示例代码。因为比较简单,所以使用到的同学们直接用就可以了,需要改进的可以参看源码,其实complex包下的Complex类的源码也并不长。呵呵1k行而已。
程序输出结果:
frac1 = 2 / 3
frac2 = 1 / 2
frac1's double form is 0.6666666666666666
frac1+frac2 = 7 / 6
frac1-frac2 = 1 / 6
frac1*frac2 = 1 / 3
frac1/frac2 = 4 / 3
frac1's inverse is 3 / 2
5 / 25 = 1 / 5
------------------------------------------------
complex z = org.apache.commons.math.complex.Complex@a8780000
complex z = 3.0+4.0i
complex x = 5.0+7.0i
abs of z = 5.0
z+x = 8.0+11.0i
z-x = -2.0-3.0i
z*x = -13.0+41.0i
sin(z) = 3.853738037919377-27.016813258003932i
cos(z) = -27.034945603074224-3.851153334811777i
conjugate of z = 3.0-4.0i
相关资料:
复数:http://zh.wikipedia.org/zh-cn/%E5%A4%8D%E6%95%B0_%28%E6%95%B0%E5%AD%A6%29
Commons math包:http://commons.apache.org/math/index.html