在Java中表示分数的最佳方法是什么?

104

我正在尝试在Java中使用分数

我想要实现算术函数。为此,我首先需要一种规范化函数的方法。我知道在没有通分之前无法将1/6和1/2相加。我将不得不添加1/6和3/6。一个天真的方法是将2/12和6/12相加,然后约分。如何以最小的性能惩罚实现共同分母?哪种算法最好?


第8版(感谢hstoerr):

改进包括:

  • equals()方法现在与compareTo()方法一致
final class Fraction extends Number {
    private int numerator;
    private int denominator;

    public Fraction(int numerator, int denominator) {
        if(denominator == 0) {
            throw new IllegalArgumentException("denominator is zero");
        }
        if(denominator < 0) {
            numerator *= -1;
            denominator *= -1;
        }
        this.numerator = numerator;
        this.denominator = denominator;
    }

    public Fraction(int numerator) {
        this.numerator = numerator;
        this.denominator = 1;
    }

    public int getNumerator() {
        return this.numerator;
    }

    public int getDenominator() {
        return this.denominator;
    }

    public byte byteValue() {
        return (byte) this.doubleValue();
    }

    public double doubleValue() {
        return ((double) numerator)/((double) denominator);
    }

    public float floatValue() {
        return (float) this.doubleValue();
    }

    public int intValue() {
        return (int) this.doubleValue();
    }

    public long longValue() {
        return (long) this.doubleValue();
    }

    public short shortValue() {
        return (short) this.doubleValue();
    }

    public boolean equals(Fraction frac) {
        return this.compareTo(frac) == 0;
    }

    public int compareTo(Fraction frac) {
        long t = this.getNumerator() * frac.getDenominator();
        long f = frac.getNumerator() * this.getDenominator();
        int result = 0;
        if(t>f) {
            result = 1;
        }
        else if(f>t) {
            result = -1;
        }
        return result;
    }
}

我已删除所有先前的版本,感谢以下人员:

35
放弃自己编写的代码,使用Apache Commons:) http://commons.apache.org/math/userguide/fraction.html - Patrick
3
如果Patrick的评论作为答案发布,它本应该得到+1的赞同。在大多数情况下,那就是正确的答案;就像《Effective Java》所说的那样,“了解并使用库”。原问题也很清晰有用。 - Jonik
当前版本实现了equals...但没有实现hashCode。如果您想将Fraction用作Map中的键,请实现hashCode。 - Xantix
还有一个bug:您想在compareTo中使用long以防止溢出,但乘法仅使用int - maaartinus
你的代码中没有 hashCode。仅定义 equals 可能会导致哈希映射中出现非常糟糕的行为(比两者都不定义还要糟糕)。请注意,定义 hashCode 需要某种规范化以保持与 equals 一致。 - maaartinus
显示剩余5条评论
26个回答

0

当定义分数时,使用这个简化欧几里得算法的函数非常有用。

 public Fraction simplify(){


     int safe;
     int h= Math.max(numerator, denominator);
     int h2 = Math.min(denominator, numerator);

     if (h == 0){

         return new Fraction(1,1);
     }

     while (h>h2 && h2>0){

          h = h - h2;
          if (h>h2){

              safe = h;
              h = h2;
              h2 = safe;

          }  

     }

  return new Fraction(numerator/h,denominator/h);

 }

0
对于工业级分数/有理数实现,我会这样实现它,以便它可以表示NaN、正无穷、负无穷,并且可选地使用操作语义与IEEE 754标准规定的浮点算术完全相同(它还简化了与浮点值之间的转换)。此外,由于与零、一和上述特殊值的比较只需要对分子和分母进行简单但组合的比较,我会添加几个isXXX和compareToXXX方法以方便使用(例如,eq0()将在幕后使用numerator == 0 && denominator != 0,而不是让客户端与一个零值实例进行比较)。一些静态预定义值(ZERO、ONE、TWO、TEN、ONE_TENTH、NAN等)也很有用,因为它们作为常量值出现在多个地方。这是我认为最好的方式。

0

可能有用的是添加一些简单的东西,比如倒数、获取余数和获取整数部分。


请提供需要翻译的英文内容。 - Jasonw
非常抱歉回复晚了,但我认为在评论答案之前需要一定数量的声望(50?),而我还没有达到这个数量... - Darth Joshua

0

初始备注:

永远不要写这个:

if ( condition ) statement;

这样好多了

if ( condition ) { statement };

只是为了养成好习惯而创建。

按照建议将类设置为不可变的,您还可以利用 double 执行 equals、hashCode 和 compareTo 操作。

这是我的快速脏版本:

public final class Fraction implements Comparable {

    private final int numerator;
    private final int denominator;
    private final Double internal;

    public static Fraction createFraction( int numerator, int denominator ) { 
        return new Fraction( numerator, denominator );
    }

    private Fraction(int numerator, int denominator) {
        this.numerator   = numerator;
        this.denominator = denominator;
        this.internal = ((double) numerator)/((double) denominator);
    }


    public int getNumerator() {
        return this.numerator;
    }

    public int getDenominator() {
        return this.denominator;
    }


    private double doubleValue() {
        return internal;
    }

    public int compareTo( Object o ) {
        if ( o instanceof Fraction ) { 
            return internal.compareTo( ((Fraction)o).internal );
        }
        return 1;
    }

    public boolean equals( Object o ) {
          if ( o instanceof Fraction ) {  
             return this.internal.equals( ((Fraction)o).internal );
          } 
          return false;
    }

    public int hashCode() { 
        return internal.hashCode();
    }



    public String toString() { 
        return String.format("%d/%d", numerator, denominator );
    }

    public static void main( String [] args ) { 
        System.out.println( Fraction.createFraction( 1 , 2 ) ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).hashCode() ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).compareTo( Fraction.createFraction(2,4) ) ) ;
        System.out.println( Fraction.createFraction( 1 , 2 ).equals( Fraction.createFraction(4,8) ) ) ;
        System.out.println( Fraction.createFraction( 3 , 9 ).equals( Fraction.createFraction(1,3) ) ) ;
    }       

}

关于静态工厂方法,如果您将Fraction子类化以处理更复杂的事情,或者决定使用池来处理最常用的对象,则稍后可能会有用。
这不一定是情况,我只是想指出。 :)
请参阅Effective Java第一项。

0

即使你有 compareTo() 方法,如果你想要使用像 Collections.sort() 这样的工具类,那么你也应该实现 Comparable 接口。

public class Fraction extends Number implements Comparable<Fraction> {
 ...
}

此外,为了实现漂亮的显示效果,我建议重写toString()方法。
public String toString() {
    return this.getNumerator() + "/" + this.getDenominator();
}

最后,我会将这个类设为公共类,这样你就可以从不同的包中使用它。

0

类 分数:

     public class Fraction {
        private int num;            // numerator 
        private int denom;          // denominator 
        // default constructor
        public Fraction() {}
        // constructor
        public Fraction( int a, int b ) {
            num = a;
            if ( b == 0 )
                throw new ZeroDenomException();
            else
                denom = b;
        }
        // return string representation of ComplexNumber
        @Override
        public String toString() {
            return "( " + num + " / " + denom + " )";
        }
        // the addition operation
        public Fraction add(Fraction x){
            return new Fraction(
                    x.num * denom + x.denom * num, x.denom * denom );
        }
        // the multiplication operation
        public Fraction multiply(Fraction x) {
            return new Fraction(x.num * num, x.denom * denom);
        } 
}

主程序:
    static void main(String[] args){
    Scanner input = new Scanner(System.in);
    System.out.println("Enter numerator and denominator of first fraction");
    int num1 =input.nextInt();
    int denom1 =input.nextInt();
    Fraction x = new Fraction(num1, denom1);
    System.out.println("Enter numerator and denominator of second fraction");
    int num2 =input.nextInt();
    int denom2 =input.nextInt();
    Fraction y = new Fraction(num2, denom2);
    Fraction result = new Fraction();
    System.out.println("Enter required operation: A (Add), M (Multiply)");
    char op = input.next().charAt(0);
    if(op == 'A') {
        result = x.add(y);
        System.out.println(x + " + " + y + " = " + result);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接