3.2 分数和复数
看其他篇章到目录选择。
我们讲到数学的计算,难免会遇到分数形式,因为实数的定义就是可以表示为一个分数的形式的数,而加入虚数的复数也是偶尔会遇到的。Commons Math包中的fraction和complex包就分别提供了方法来表示这两种数。
首先来看一个接口FieldElement<T>,这个接口定义了一个泛型参数,其具体内容定义了5组方法,分别是加减乘除运算和getField()方法。具体Field<T>是什么,这里就不研究了,因为我发现暂时还没有用到它。呵呵。
像这样的无算法和实现的类,具体就是看示例来观察它的运算行为了。
正式开始,先看看Fraction类,在fraction子包下的Fraction类继承了java中的Number类,同时实现了接口FieldElement<Fraction>。Fraction的构造方法有5个,支持给定一个实数来构建分数,也支持给定分子分母的构建方法。具体的见代码吧,因为代码已经非常直观了。
1/** *//**
2 *
3 */
4package algorithm.math;
5
6import org.apache.commons.math.complex.Complex;
7import org.apache.commons.math.fraction.Fraction;
8import org.apache.commons.math.fraction.FractionConversionException;
9
10/** *//**
11 * @author Jia Yu
12 * @date 2010-11-29
13 */
14public 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型的成员变量。我忍不住要把构造方法的源码贴上来:
1private 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