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