在上一篇《法莱数列》数列中简要地介绍了法莱数列的特性、如何生成一组法莱数列,以及如何获得指定最大分母的法莱数列的个数。
在这一篇中,我们采用 Java 语言通过其特性来生成法莱数列。由于涉及分数运算,分数类直接就使用 Apache Commons Lang,这个包里有着许多有用的工具类,众多的 Java 开源产品中有许多也引用了这一类库,在这里仅用到 Fraction 类。
为了让各个功能进行模块化管理,设计了三个类:
- Farey:用于生成法莱数列的类,其中有获得所有指定数的法莱数列和数列个数的一些方法;
- FractionUtil:分数的工具类,这里主要有分数数组的成批输出和按照数学的分式形式进行输出等方法;
- FareyTest:用于测试/生成法莱数列。
下面是这三个文件的源代码:
FareyTest.java
import
org.apache.commons.lang.math.Fraction;
public class FareyTest {
public static void main(String[] args) {
// 构造一个分母不大于5的法莱数列
Farey farey = new Farey(5 );
// 获得法莱数列
Fraction[] fareys = farey.getFaleys();
// 以单行形式输出
FractionUtil.toString(fareys);
// 以数学分式形式输出
FractionUtil.toFractionString(fareys);
}
}
public class FareyTest {
public static void main(String[] args) {
// 构造一个分母不大于5的法莱数列
Farey farey = new Farey(5 );
// 获得法莱数列
Fraction[] fareys = farey.getFaleys();
// 以单行形式输出
FractionUtil.toString(fareys);
// 以数学分式形式输出
FractionUtil.toFractionString(fareys);
}
}
Farey.java
import
org.apache.commons.lang.math.Fraction;
public class Farey {
/**
* 法莱数列最大的分母数
*/
private int maxDenominator = 0 ;
/**
* 法莱数列的个数
*/
private int groups = 0 ;
/**
* 构造一个分母不大于max的法莱数列
* @param max
*/
public Farey(int max) {
setMax(max);
}
public int getMaxDenominator() {
return this .maxDenominator;
}
public void setMax(int maxDenominator) {
// 法莱数列的最大的分母必须不小于2
if (maxDenominator < 2 ) {
throw new IllegalArgumentException("数列分母的最大数不能小于2" );
}
this.maxDenominator = maxDenominator;
this.groups = 0 ;
}
/**
* 获得分母不大于max法莱数列的个数
* @return 法莱数列的个数
*/
public int getFareyNumbers() {
if (groups != 0 ) {
return groups;
}
for (int i = 2; i <= maxDenominator; i++ ) {
groups++ ;
for (int j = 2; j < i; j++ ) {
if (gcd(i, j) == 1 ) {
groups++ ;
}
}
}
return groups;
}
/**
* 获得分母不大于max的法莱数列分数数组
* @return 法莱数列分数数组
*/
public Fraction[] getFaleys() {
int groups = getFareyNumbers();
Fraction[] fareys = new Fraction[groups];
fareys[0] = Fraction.getFraction(1 , maxDenominator);
if (groups > 1 ) {
fareys[1] = Fraction.getFraction(1, maxDenominator - 1 );
}
for (int i = 2; i < groups; i++ ) {
fareys[i] = getNextFarey(fareys[i - 2], fareys[i - 1 ]);
}
return fareys;
}
/**
* 根据法莱数列的前两个分数,获得第三个分数
* @param f1 第一个分数
* @param f2 第二个分数
* @return 法莱数列的第三个分数
*/
private Fraction getNextFarey(Fraction f1, Fraction f2) {
int multiple = (maxDenominator + f1.getDenominator()) / f2.getDenominator();
int numerator = multiple * f2.getNumerator() - f1.getNumerator();
int denominator = multiple * f2.getDenominator() - f1.getDenominator();
return Fraction.getFraction(numerator, denominator).reduce();
}
/**
* 求两个数的最大公约数,经典算法哦
*/
private int gcd(int m, int n) {
if (n == 0 ) {
return m;
}
return gcd(n, m % n);
}
}
public class Farey {
/**
* 法莱数列最大的分母数
*/
private int maxDenominator = 0 ;
/**
* 法莱数列的个数
*/
private int groups = 0 ;
/**
* 构造一个分母不大于max的法莱数列
* @param max
*/
public Farey(int max) {
setMax(max);
}
public int getMaxDenominator() {
return this .maxDenominator;
}
public void setMax(int maxDenominator) {
// 法莱数列的最大的分母必须不小于2
if (maxDenominator < 2 ) {
throw new IllegalArgumentException("数列分母的最大数不能小于2" );
}
this.maxDenominator = maxDenominator;
this.groups = 0 ;
}
/**
* 获得分母不大于max法莱数列的个数
* @return 法莱数列的个数
*/
public int getFareyNumbers() {
if (groups != 0 ) {
return groups;
}
for (int i = 2; i <= maxDenominator; i++ ) {
groups++ ;
for (int j = 2; j < i; j++ ) {
if (gcd(i, j) == 1 ) {
groups++ ;
}
}
}
return groups;
}
/**
* 获得分母不大于max的法莱数列分数数组
* @return 法莱数列分数数组
*/
public Fraction[] getFaleys() {
int groups = getFareyNumbers();
Fraction[] fareys = new Fraction[groups];
fareys[0] = Fraction.getFraction(1 , maxDenominator);
if (groups > 1 ) {
fareys[1] = Fraction.getFraction(1, maxDenominator - 1 );
}
for (int i = 2; i < groups; i++ ) {
fareys[i] = getNextFarey(fareys[i - 2], fareys[i - 1 ]);
}
return fareys;
}
/**
* 根据法莱数列的前两个分数,获得第三个分数
* @param f1 第一个分数
* @param f2 第二个分数
* @return 法莱数列的第三个分数
*/
private Fraction getNextFarey(Fraction f1, Fraction f2) {
int multiple = (maxDenominator + f1.getDenominator()) / f2.getDenominator();
int numerator = multiple * f2.getNumerator() - f1.getNumerator();
int denominator = multiple * f2.getDenominator() - f1.getDenominator();
return Fraction.getFraction(numerator, denominator).reduce();
}
/**
* 求两个数的最大公约数,经典算法哦
*/
private int gcd(int m, int n) {
if (n == 0 ) {
return m;
}
return gcd(n, m % n);
}
}
FractionUtil.java
import
org.apache.commons.lang.math.Fraction;
public class FractionUtil {
/**
* 以单行形式输出分数数组,以逗号分隔
* @param fracs 分数数组
*/
public static void toString(Fraction[] fracs) {
for (int i = 0, k = fracs.length - 1; i < k; i++ ) {
System.out.print(fracs[i].toString() + ", " );
}
System.out.println(fracs[fracs.length - 1 ].toString());
}
/**
* 以普通的分数形式(分子、分数线、分母的上中下式)输出分数数组
* @param fracs 分数数组
*/
public static void toFractionString(Fraction[] fracs) {
char[] line = { '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' };
char[] space = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
int[] width = new int [fracs.length];
int[] numeratorWidth = new int [fracs.length];
int[] denominatorWidth = new int [fracs.length];
for (int i = 0; i < fracs.length; i++ ) {
numeratorWidth[i] = width(fracs[i].getNumerator());
denominatorWidth[i] = width(fracs[i].getDenominator());
width[i] = Math.max(numeratorWidth[i], denominatorWidth[i]);
}
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(" "
+ new String(space, 0, width[i] - numeratorWidth[i])
+ fracs[i].getNumerator() + " " );
}
System.out.println();
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(new String(line, 0, width[i] + 2) + " " );
}
System.out.println();
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(" "
+ new String(space, 0, width[i] - denominatorWidth[i])
+ fracs[i].getDenominator() + " " );
}
System.out.println();
}
/**
* 获得一个数字的位数
*/
private static int width(int num) {
int width = 0 ;
while (num != 0 ) {
num /= 10 ;
width++ ;
}
return width;
}
private FractionUtil(){
}
}
public class FractionUtil {
/**
* 以单行形式输出分数数组,以逗号分隔
* @param fracs 分数数组
*/
public static void toString(Fraction[] fracs) {
for (int i = 0, k = fracs.length - 1; i < k; i++ ) {
System.out.print(fracs[i].toString() + ", " );
}
System.out.println(fracs[fracs.length - 1 ].toString());
}
/**
* 以普通的分数形式(分子、分数线、分母的上中下式)输出分数数组
* @param fracs 分数数组
*/
public static void toFractionString(Fraction[] fracs) {
char[] line = { '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' };
char[] space = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
int[] width = new int [fracs.length];
int[] numeratorWidth = new int [fracs.length];
int[] denominatorWidth = new int [fracs.length];
for (int i = 0; i < fracs.length; i++ ) {
numeratorWidth[i] = width(fracs[i].getNumerator());
denominatorWidth[i] = width(fracs[i].getDenominator());
width[i] = Math.max(numeratorWidth[i], denominatorWidth[i]);
}
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(" "
+ new String(space, 0, width[i] - numeratorWidth[i])
+ fracs[i].getNumerator() + " " );
}
System.out.println();
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(new String(line, 0, width[i] + 2) + " " );
}
System.out.println();
for (int i = 0; i < fracs.length; i++ ) {
System.out.print(" "
+ new String(space, 0, width[i] - denominatorWidth[i])
+ fracs[i].getDenominator() + " " );
}
System.out.println();
}
/**
* 获得一个数字的位数
*/
private static int width(int num) {
int width = 0 ;
while (num != 0 ) {
num /= 10 ;
width++ ;
}
return width;
}
private FractionUtil(){
}
}