1

I am trying to find and print the number of occurrences of fractions from a file in my program. Fractions that simplify down to the same number counts as an occurrence, so 12/6 counts for 6/3 as well. So far, I have separated the fractions into numerator and denominator in separate arrays. The fractions I have in the numerator and denominator came from a separate array that I received from a file. I am having trouble trying to figure out how to simplify the fractions and also find the total occurrence of them. This is what I have so far:

String[] split = new String[2]; //String that holds numerator and denominator
    int[] numerator = new int[100];
    int[] denominator = new int[100];

    for(int i = 0; i < numOfFractions; ++i) { //Loop through # of Lines
        split = fractions[i].split("/");  //Split the fractions at the /
        System.out.println("Test here " + fractions[i]);  //TODO --> test
        numerator[i] = Integer.parseInt(split[0]);  //Numerator
        System.out.println("Numerator = " + numerator[i]);  //TODO --> test
        denominator[i] = Integer.parseInt(split[1]);  //Denominator
        System.out.println("Denominator = " + denominator[i] + "\n");    //TODO --> test
    }

}

These are the fractions obtained from a file. Each fraction is on its own line, and can assume every fraction will be (A/B) format

6/3
4/2
5/9
80/90
800/900
5/5
1/2
1/3
1/1
1/4
2/7
2/8
2/9
Branden
  • 29
  • 2
  • What exactly do you mean *12/6 counts for 6/3 as well*? Does that relationship also exist between 12/6 and 8/4 and 4/2 and 2/1? – Bohemian Jan 07 '22 at 20:44

2 Answers2

2

You can reduce them by finding the greatest common divisor of the numerator and the denominator. You can do it like so.

public static int gcd(int r, int s) {
    while (s > 0) {
        int t = r % s;
        r = s;
        s = t;
    }
    return r;
}

for 27/12

int d = gcd(27,12); // returns 3

27/3 = 9
12/3 = 4

so the reduced fraction is 9/4. So every equal fraction will reduce to the same value when the numerator and denominator are divided by the GCD of those numbers.

It uses Euclid's Algorithm

Here is how it might be applied in your case.

  • create a map to hold the reduced fraction as key and an Integer for the count.
  • then convert the fraction to a numeric value and divide by the GCD
  • then store the reduced fraction as a string key and increment the count for that key.
    
String[] s = {"10/2", "27/3", "19/4",  "15/3", "12/4",  "3/5", "9/15"};
    
Map<String, Integer> fractions = new HashMap<>();
        
        String[] s = { "10/2", "27/3", "19/4", "15/3", "12/4", "3/5",
                "9/15" };
        
        for (String vv : s) {
            String[] nd = vv.split("/");
            int n = Integer.parseInt(nd[0]);
            int d = Integer.parseInt(nd[1]);
            int div = gcd(n, d);
            n /= div;
            d /= div;
            String reduced = String.format("%d/%d", n, d);
            fractions.compute(reduced, (k,v)->v == null ? 1 : v + 1);
                
        }
        fractions.entrySet().forEach(System.out::println);
}

prints

3/1=1
5/1=2
3/5=2
19/4=1
9/1=1

Note. What
fractions.compute(reduced, (k,v)->v == null ? 1 : v + 1);
does is to check if the value is null. If so, set the count to 1, otherwise add 1 to the existing count.

WJS
  • 36,363
  • 4
  • 24
  • 39
0

I would create a Fraction class as below. I would override equals() and hashCode() methods, so I can use this class with Map.

public class Fraction {
    int nominator;
    int denominator;

    public Fraction(int nominator, int denominator) {
        this.nominator = nominator;
        this.denominator = denominator;
    }

    @Override
    public String toString() {
        return nominator + "/" + denominator;
    }  
    
    
    /**
     * The method checks if two fractions are equal. 
     * Example 1: Fraction 1 is 5/3 and Fraction 2 is 7/4.
     * The method checks if 5X4 == 7X3 and returns false.
     * Example 2: Fraction 1 is 12/6 and Fraction 2 is 8/4.
     * The method checks 12X4 == 8X6 and returns true.
     * @param obj
     * @return 
     */
    @Override
    public boolean equals(Object obj){
        if(obj.getClass() != this.getClass()){
            return false;
        }
        Fraction f = (Fraction)obj;
        int nominator1 = this.getNominator();
        int nominator2 = f.getNominator();
        int denominator1 = this.getDenominator();
        int denominator2 = f.getDenominator();
        nominator1 = nominator1 * denominator2;
        nominator2 = nominator2 * denominator1;
        return nominator1 == nominator2;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        if(nominator > denominator){
            hash *= getIntegerPartOfFraction(nominator, denominator);
        } else if (denominator > nominator){
            hash *= getIntegerPartOfFraction(denominator, nominator);
        }
        //System.out.println(toString() + ",hash:" + hash);
        return hash;
    }
    
    private int getIntegerPartOfFraction(int big, int small){
        int value = 0;
        while(small < big){
            big -= small;
            value++;
        }
        return value;
    }

    public int getNominator() {
        return nominator;
    }

    public int getDenominator() {
        return denominator;
    }
}

Then, I would create Fraction objects from your data input file. I understand you already have retrieved integer values of nominators and denominators. The following code creates a new List with Fraction objects.

   List<Fraction> fractions = new ArrayList(){{
       add(new Fraction(1, 2));
       add(new Fraction(2, 4));
       add(new Fraction(4, 8));
       add(new Fraction(8, 16));
       add(new Fraction(5, 2));
       add(new Fraction(10, 4));
       add(new Fraction(20, 8));
       add(new Fraction(1, 2));
       add(new Fraction(3, 8));
       add(new Fraction(9, 24));      
   }};

Once you populate the list with your input data, you can use a Map to find and print the result.

       Map<Fraction,Integer> map = new HashMap<>();
       for(Fraction f: fractions){
           if(map.containsKey(f)){
               int value = map.get(f);
               value++;
               map.put(f, value);
           } else {
               map.put(f, 1);
           }
       }
       
       System.out.println(map);

Here is the output for the given fractions.

{1/2=5, 5/2=3, 3/8=2}
Gaurang
  • 67
  • 8
  • 1
    The hashCode implementation is buggy, since it will return different values for equivalent fractions – Rocco Jan 07 '22 at 22:06
  • And in your equals method, `denominator2` should be assigned `f.getDenominator()` But that is irrelevant until you fix your hashCode. – WJS Jan 07 '22 at 22:51
  • 1
    Thank you Rocco, WJS for pointing out the bug and typo. I have updated the code. – Gaurang Jan 08 '22 at 09:22