Working with Fractions in Java

by Michael Gilleland

Introduction
Egyptian Fractions
Farey Sequences
Continued Fractions
Big Fractions
Harmonic Numbers
Wolstenholme's Theorem
Fractran Algorithm
Conway's Prime Game

Introduction

In his book Mathematics for the Nonmathematician (NY: Dover, 1985), pp. 64-65, Morris Kline points out:

The disappointing feature of the decimal representation of fractions is that some simple fractions cannot be represented as fractions with a finite number of digits. Thus when we seek to express 1/3 as a decimal, we find that neither 0.3, nor 0.33, nor 0.333, and so on, suffices. All one can say in this and similar cases is that by carrying more and more decimal digits, one comes closer and closer to the fraction, but no finite number of digits will ever be the exact answer. This fact is expressed by the notation 1/3 = 0.333..., where the dots indicate that we must keep on adding threes to approach the fraction 1/3 more and more closely.

In most programming languages, including Java, this leads to well-known problems, as the following code fragment illustrates:
double f = 1.0/7.0;
double sum = 0.0;
for (int i = 0; i < 7; i++) {
  sum += f;
}
System.out.println (sum);
We have attempted to compute the sum of seven sevenths, which should of course be one. But when these statements are executed, the output is:
0.9999999999999998

Close, but no cigar. Although the workarounds to such problems are as well-known as the problems themselves, it often pays to dispense with the decimal representation of fractions altogether, and instead deal with fractions simply as fractions. Doug Lea of the State University of New York at Oswego has written a very useful Java class, Fraction.java, which allows us to do just that. The source code for Fraction.java is available here and is part of package EDU.oswego.cs.dl.util.concurrent.misc -- if I ruled the universe, it would be part of core package java.math.

Let's rewrite the code fragment shown above with its equivalent using Fraction.java:

Fraction f = new Fraction (1, 7);
Fraction sum = new Fraction (0, 1);
for (int i = 0; i < 7; i++) {
  sum = sum.plus (f);
}
System.out.println (sum);
This time, the output is:
1

This paper shows how easy it is, with the help of Fraction.java, to explore some interesting highways and byways of fractions, such as Egyptian fractions, Farey sequences, and continued fractions.

In some cases, we need fractions with very large numerators and denominators, and so this paper also contains a modification of Fraction.java, called BigFraction.java. With BigFraction.java as a tool, we also explore harmonic numbers, test numbers for primality using Wolstenholme's Theorem, and implement an unusual algorithm (Fractran) which can be used to generate prime numbers.

The mathematics in this paper can all be understood, with a little patience, by anyone who grasps the simple concepts of fractions as learned in elementary school.

All source code in this paper is free for you to use as you wish, with no restrictions and no guarantees.


Egyptian Fractions

The ancient Egyptians represented fractions such as 3/5 as a sum of distinct unit fractions, that is, fractions whose numerators are one, e.g. 1/2 and 1/10. The sum 1/5 + 1/5 + 1/5 would be unacceptable, because the unit fractions (while adding up to 3/5) are not distinct.

Egyptian fractions have been the subject of much research by mathematicians, who have devised a number of clever algorithms to find Egyptian fractions for a given proper fraction. One of the easiest to understand is the so-called greedy algorithm, which works like this:

  1. Start with an empty list, set denominator = 2 and remainder = fraction we're trying to decompose.
  2. Create a candidate unit fraction = 1/denominator.
  3. Increment denominator.
  4. If candidate > remainder, go to step 2.
  5. Add candidate to list.
  6. Subtract candidate from remainder. Result is new remainder.
  7. If remainder is 0, go to step 10.
  8. If remainder is a unit fraction, add it to the list and go to step 10.
  9. Go to to step 2.
  10. Hurrah, we're done! List contains Egyptian fractions adding up to original fraction.

Let's "play computer" and follow these steps in an attempt to decompose 3/7 into Egyptian fractions.

Steps Denominator Remainder Candidate List
1 2 3/7   []
2 2 3/7 1/2 []
3-4 3 3/7 1/2 []
2 3 3/7 1/3 []
3-4 4 3/7 1/3 []
5 4 3/7 1/3 [1/3]
6-9 4 2/21 1/3 [1/3]
2 4 2/21 1/4 [1/3]
3-4 5 2/21 1/4 [1/3]
2 5 2/21 1/5 [1/3]
3-4 6 2/21 1/5 [1/3]
2 6 2/21 1/5 [1/3]
3-4 7 2/21 1/6 [1/3]
2 7 2/21 1/7 [1/3]
3-4 8 2/21 1/7 [1/3]
2 8 2/21 1/8 [1/3]
3-4 9 2/21 1/8 [1/3]
2 9 2/21 1/9 [1/3]
3-4 10 2/21 1/9 [1/3]
2 10 2/21 1/10 [1/3]
3-4 11 2/21 1/10 [1/3]
2 11 2/21 1/11 [1/3]
3-4 12 2/21 1/11 [1/3]
5 12 1/231 1/11 [1/3, 1/11]
6-7 12 1/231 1/11 [1/3, 1/11]
8 12 1/231 1/11 [1/3, 1/11, 1/231]
10 12 1/231 1/11 [1/3, 1/11, 1/231]

The following Java class, EgyptianFractions.java, computes Egyptian fractions for a specified fraction between zero and one using the greedy algorithm discussed above.

//------------------------------------------------
// Egyptian fractions have a numerator of 1.
// Any fraction can be decomposed into a series of
// unique Egyptian fractions.
//------------------------------------------------

import java.util.Vector;
import EDU.oswego.cs.dl.util.concurrent.misc.Fraction; 

public abstract class EgyptianFractions {

  //----------
  // Constants
  //----------

  private static Fraction ZERO = new Fraction (0, 1);
  private static Fraction ONE = new Fraction (1, 1);

  //---------------------------------------
  // Get sum of Vector of Fraction objects.
  // Used to check accuracy of algorithm.
  //---------------------------------------

  public static Fraction getSum (Vector v) {
    Fraction sum = new Fraction (ZERO);
    for (int i = 0; i < v.size (); i++) {
      sum = sum.plus ((Fraction) v.elementAt (i));
    }
    return sum;
  }

  //---------------------------------
  // Decompose using greedy algorithm
  //---------------------------------

  public static Vector greedyAlgorithm (Fraction f) {

    // Make sure we're starting out with a
    // fraction between 0 and 1, exclusive

    if (f.compareTo (ZERO) <= 0) {
      throw new IllegalArgumentException (f.toString ());
    }
    if (f.compareTo (ONE) >= 0) {
      throw new IllegalArgumentException (f.toString ());
    }

    Fraction remainder = new Fraction (f); // remainder
    long denominator = 2; // first candidate will be 1/2
    Vector v = new Vector (); // list of successful candidates
    boolean done = false; // completion flag

    while (!done) {

      // Get the next candidate

      Fraction candidate = new Fraction (1, denominator);

      // If the candidate is less than the current remainder,
      // add it to the "successful" list and subtract it from the remainder

      if (candidate.compareTo (remainder) <= 0) {
        v.addElement (candidate);
        remainder = remainder.minus (candidate);
      }

      // If the remainder is 0, we're done.

      if (remainder.equals (ZERO)) {
        done = true;
      }
      else {

        // If the remainder's numerator is 1, add it to
        // the list and we're done.

        if (remainder.numerator () == 1) {
          v.addElement (remainder);
          done = true;
        }
        else {

          // Increment the denominator for the next candidate

          denominator++;
        }
      }
    }

    return v;

  }

  //-----
  // Test
  //-----

  public static void main (String[] args) {
    Fraction f = new Fraction (3, 7);
    Vector v = EgyptianFractions.greedyAlgorithm (f);
    System.out.println (v.toString ());
    System.out.println (EgyptianFractions.getSum (v).toString ());
  }

}

Here are some useful links on Egyptian fractions. Those by Ron Knott and Jim Loy are the easiest to understand.

In a category by themselves are several thought-provoking pages on unit fractions by Kevin Brown: Martin Gardner has a chapter on Egyptian fractions in his book Fractal Music, Hypercards and More... (NY: W.H. Freeman, 1992), pp. 100-109, and there is a discussion of unit fractions in Paul Hoffman, The Man Who Loved Only Numbers (NY: Hyperion, 1998), pp. 153-157.


Farey Sequences

Suppose you were asked for a list of all fractions between 0 and 1 inclusive, whose denominator does not exceed a given number n.

A list like this is known as a Farey sequence. Different lists are distinguished by their "order", that is, the number n which represents the largest denominator. The following diagram shows all Farey sequences from order 1 to 6.
[0/1,                                                        1/1]
[0/1,                          1/2,                          1/1]
[0/1,                1/3,      1/2,      2/3,                1/1]
[0/1,           1/4, 1/3,      1/2,      2/3, 3/4,           1/1]
[0/1,      1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5,      1/1]
[0/1, 1/6, 1/5, 1/4, 1/3, 2/5, 1/2, 3/5, 2/3, 3/4, 4/5, 5/6, 1/1]
Inspection of this illustration reveals many curious properties of Farey sequences. We'll just look at a couple.

For every sequence of order >= 2, the fraction 1/2 stands in the middle. Any two terms equidistant from 1/2 are complementary, that is to say, they add up to 1. Looking at the Farey sequence of order 6, we see that

Another curious property is this. Choose any two adjacent terms, say 1/3 and 2/5 in the Farey sequence of order 6. Their difference is equal to the reciprocal of the product of their denominators. For example, the difference between 1/3 and 2/5 is 1/15. The product of the denominators of 1/3 and 2/5 is 15, whose reciprocal is also 1/15.

The following Java class, FareySequence.java, generates Farey sequences.

//-------------------------
// Farey sequence generator
//-------------------------

import java.util.Vector;
import EDU.oswego.cs.dl.util.concurrent.misc.Fraction; 

public class FareySequence {

  private Vector seq;
  private long order;

  //------
  // Reset
  //------

  public void reset () {
    seq = null;
    order = 0;
  }

  //--------------------------------------------
  // The mediant of a/b and c/d is (a+c) / (b+d)
  //--------------------------------------------

  public static Fraction getMediant (Fraction f1, Fraction f2) {
    return new Fraction (f1.numerator () + f2.numerator (),
                         f1.denominator () + f2.denominator ());
  }

  public static boolean isMediant (Fraction candidate, Fraction f1, Fraction f2) {
    return (candidate.numerator () == f1.numerator () + f2.numerator ()) &&
           (candidate.denominator () == f1.denominator () + f2.denominator ());
  }

  //------------------
  // Get next sequence
  //------------------

  public Vector getNext () {

    if (order == 0) {
      seq = new Vector ();
      seq.addElement (new Fraction (0, 1));
      seq.addElement (new Fraction (1, 1));
      order = 1;
    }
    else {
      order++;
      Vector v = new Vector ();
      for (int i = 0; i < seq.size (); i++) {
        Fraction f1 = (Fraction) seq.elementAt (i);
        v.addElement (f1);
        if (i < seq.size () - 1) {
          Fraction f2 = (Fraction) seq.elementAt (i + 1);
          if ((f1.denominator () + f2.denominator ()) <= order) {
            Fraction child = getMediant (f1, f2);
            v.addElement (child);
          }
        }
      }
      seq = v;
    }
    return seq;

  }

  //--------------------------------------------
  // Get the order of the last sequence computed
  //--------------------------------------------

  public long getOrder () {
    return order;
  }

  //--------------------------------
  // Get sequence of specified order
  //--------------------------------

  public Vector getSequence (long order) {
    if (order <= 0) {
      return null;
    }
    reset ();
    while (this.order <= order ) {
      getNext ();
    }
    return seq;
  }

  //-----
  // Test
  //-----

  public static void main (String[] args) {
    FareySequence fs = new FareySequence ();
    Vector next;
    for (int i = 0; i < 10; i++) {
      next = fs.getNext ();
      System.out.println (next);
    }
    fs.reset ();
    System.out.println (fs.getSequence (9));
  }

}

Here are some useful links on Farey sequences.

Here are also some references from books:


Continued Fractions

A general continued fraction has the form:

           a1                                
b0 + --------------------------------------             
                a2                        
      b1 + --------------------------------             
                        a3                
            b2 + --------------------------             
                        b3 +              
                              . . .       
                                       an 
                                    + -----
                                       bn 

If this looks intimidating, let's consider the slightly less complicated simple continued fraction, which has the form:

           1                                
b0 + --------------------------------------             
                1                        
      b1 + --------------------------------             
                        1                
            b2 + --------------------------             
                        b3 +              
                              . . .       
                                       1 
                                    + -----
                                       bn 

If you compare the general with the simple form, you'll see that we've just replaced all of the "b"s with "1"s. If this still looks intimidating, let's look at a concrete example. Any rational number can be represented as a simple continued fraction, and so we'll try 45/16. Its representation as a simple continued fraction looks like this:

        1                                
2 + ---------------             
             1                        
      1 + ---------             
                 1                
            4 + ---             
                 3              

In order to evaluate this still rather complicated-looking expression, let's start at the bottom and work upwards.

Mathematicians are always looking for compact notations, and they usually express a simple continued fraction in list form, retaining only the "a" values. [2; 1, 4, 3] is the list form of the continued fraction for 45/16. Here are a few more examples of list forms of continued fractions: Note that the list form for fractions less than 1 always has 0 as the first member, as we would expect.

The following Java class, ContinuedFractions.java, converts to and from simple continued fractions.

//------------------------------------------------
// Converts to and from simple continued fractions
//------------------------------------------------

import java.util.Vector;
import EDU.oswego.cs.dl.util.concurrent.misc.Fraction; 

public abstract class ContinuedFractions {

  //-----------------------------------------------------
  // Given a proper fraction, return its simple continued
  // fraction representation in list form
  //-----------------------------------------------------

  public static Vector to (Fraction f) {
    Vector v = new Vector ();
    if (f.denominator () == 1) {
      v.addElement (new Long (f.numerator ()));
    }
    else if (f.numerator () == 1) {
      v.addElement (new Long (0));
      v.addElement (new Long (f.denominator ()));
    }
    else {
      Fraction x = new Fraction (f);
      long quotient, remainder;
      while (x.denominator () > 1) {
        quotient = x.numerator () / x.denominator ();
        remainder = x.numerator () % x.denominator ();
        v.addElement (new Long (quotient));
        x = new Fraction (x.denominator (), remainder);
        if (x.denominator () == 1) {
          v.addElement (new Long (x.numerator ()));
        }
      }
    }
    return v;
  }

  //-----------------------------------------------
  // Given a continued fraction in list form,
  // return the proper fraction which it represents
  //-----------------------------------------------

  public static Fraction from (Vector v) {
    if (v.size () == 1) {
      return new Fraction (getLong (v, 0), 1);
    }
    Fraction sum = null;
    Fraction f;
    long a;
    for (int i = v.size () - 1; i >= 1; i--) {
      a = getLong (v, i - 1);
      if (sum == null) {
        Fraction f1 = new Fraction (a, 1);
        Fraction f2 = new Fraction (1, getLong (v, i));
        sum = f1.plus (f2);
      }
      else {
        sum = sum.inverse ().plus (a);
      }
    }
    return sum;
  }

  //-----------------------------------------------
  // Utility function to extract long from a Vector
  //-----------------------------------------------

  private static long getLong (Vector v, int idx) {
    Long longObj = (Long) v.elementAt (idx);
    return longObj.longValue ();
  }

  //-----
  // Test
  //-----

  public static void main (String[] args) {
    Fraction fractions[] = {new Fraction (45, 16),
                            new Fraction (1, 3),
                            new Fraction (2, 1),
                            new Fraction (3, 5),
                            new Fraction (8, 16)};
    Vector v;
    Fraction f;
    for (int i = 0; i < fractions.length; i++) {
      f = fractions[i];
      v = ContinuedFractions.to (f);
      System.out.println (v.toString ());
      f = ContinuedFractions.from (v);
      System.out.println (f.toString ());
    }
  }

}

Here are some useful links about continued fractions. The best introductions for amateurs are those by Ron Knott and Adam Van Tuyl.

Here are also some references from books: Some texts (e.g. Klaf) use the terminology chain fraction, rather than continued fraction.


Big Fractions

If you work with fractions having large numerators and denominators, you may find negative fractions cropping up where you don't expect them, due to the "wraparound" phenomenon associated with the Java long data type. If you add one to the biggest possible value of a Java long, the result is the smallest possible value of a Java long, as the following code fragment shows:
System.out.println (Long.MAX_VALUE);
System.out.println (Long.MAX_VALUE + 1);
System.out.println (Long.MIN_VALUE);
The output is:
9223372036854775807
-9223372036854775808
-9223372036854775808
I encountered this snag when I was adding terms in the harmonic series (1/1 + 1/2 + 1/3 + 1/4 ...), and to get around it I wrote a new BigFraction.java class, in which the numerator and denominator are of type java.math.BigInteger.
//-------------------------------------------------------
// An adaptation of Doug Lea's Fraction.java with
// BigInteger (instead of long) numerator and denominator
//-------------------------------------------------------

import java.math.BigDecimal;
import java.math.BigInteger;
import java.io.Serializable;

public class BigFraction implements Cloneable, Comparable, Serializable {
  protected final BigInteger numerator_;
  protected final BigInteger denominator_;

  //-----------------
  // Accessor methods
  //-----------------

  public BigInteger numerator () {
    return numerator_;
  }

  public BigInteger denominator () {
    return denominator_;
  }

  //-------------
  // Constructors
  //-------------

  public BigFraction (BigInteger num, BigInteger den) {
    // Reduce to lowest terms
    boolean numNonnegative = gteq (num, BigInteger.ZERO);
    boolean denNonnegative = gteq (den, BigInteger.ZERO);
    BigInteger a = numNonnegative? num : num.negate ();
    BigInteger b = denNonnegative? den : den.negate ();
    BigInteger g = a.gcd (b);
    if (numNonnegative == denNonnegative) {
      numerator_ = a.divide (g);
    }
    else {
      numerator_ = a.negate ().divide (g);
    }
    denominator_ = b.divide (g);
  }

  public BigFraction (BigFraction f) {
    numerator_ = f.numerator();
    denominator_ = f.denominator();
  }

  public BigFraction (String s) {
    this (new BigInteger (s.substring (0, s.indexOf ('/'))),
          new BigInteger (s.substring (s.indexOf ('/') + 1)));
  }

  public BigFraction (long num, long den) {
    this (new BigInteger (Long.toString (num)),
          new BigInteger (Long.toString (den)));
  }

  //------------------
  // Override toString
  //------------------

  public String toString () {
    return numerator ().toString () + "/" +
           denominator ().toString ();
  }

  //--------------------------------
  // Required to implement Cloneable
  //--------------------------------

  public Object clone () {
    return new BigFraction (this);
  }

  //----------------------------
  // Utility comparison routines
  //----------------------------

  private boolean gt (BigInteger x, BigInteger y) {
    return x.compareTo (y) > 0;
  }

  private boolean gteq (BigInteger x, BigInteger y) {
    return x.compareTo (y) >= 0;
  }

  private boolean lt (BigInteger x, BigInteger y) {
    return x.compareTo (y) < 0;
  }

  private boolean lteq (BigInteger x, BigInteger y) {
    return x.compareTo (y) <= 0;
  }

  //------------
  // Get minimum
  //------------

  public BigFraction min (BigFraction val) {
    if (compareTo (val) <= 0) {
      return this;
    }
    else {
      return val;
    }
  }

  //------------
  // Get maximum
  //------------

  public BigFraction max (BigFraction val) {
    if (compareTo (val) > 0) {
      return this;
    }
    else {
      return val;
    }
  }

  //-------------------------------------------------------
  // Convert to BigDecimal
  // Rounding mode is any of BigDecimal.ROUND_xxx constants
  //-------------------------------------------------------

  public BigDecimal asBigDecimal (int scale, int roundingMode) {
    BigDecimal num = new BigDecimal (numerator ());
    BigDecimal den = new BigDecimal (denominator ());
    return num.divide (den, scale, roundingMode);
  }

  //------------------
  // Get negated value
  //------------------

  public BigFraction negate () {
    return new BigFraction (numerator ().negate (), denominator ());
  }

  //---------------------------
  // Get multiplicative inverse
  //---------------------------

  public BigFraction inverse () {
    return new BigFraction (denominator (), numerator ());
  }

  //----
  // Add
  //----

  public BigFraction add (BigFraction b) {
    BigInteger an = numerator ();
    BigInteger ad = denominator ();
    BigInteger bn = b.numerator ();
    BigInteger bd = b.denominator ();
    return new BigFraction (an.multiply (bd).add (bn.multiply (ad)), ad.multiply (bd));
  }

  public BigFraction add (BigInteger n) {
    return add (new BigFraction (n, BigInteger.ONE));
  }

  public BigFraction add (long n) {
    return add (new BigInteger (Long.toString (n)));
  }

  //---------
  // Subtract
  //---------

  public BigFraction subtract (BigFraction b) {
    BigInteger an = numerator();
    BigInteger ad = denominator();
    BigInteger bn = b.numerator();
    BigInteger bd = b.denominator();
    return new BigFraction(an.multiply (bd).subtract (bn.multiply (ad)), ad.multiply (bd));
  }

  public BigFraction subtract (BigInteger n) {
    return subtract (new BigFraction (n, BigInteger.ONE));
  }

  public BigFraction subtract (long n) {
    return subtract (new BigInteger (Long.toString (n)));
  }

  //---------
  // Multiply
  //---------

  public BigFraction multiply (BigFraction b) {
    BigInteger an = numerator();
    BigInteger ad = denominator();
    BigInteger bn = b.numerator();
    BigInteger bd = b.denominator();
    return new BigFraction (an.multiply (bn), ad.multiply (bd));
  }

  public BigFraction multiply (BigInteger n) {
    return multiply (new BigFraction (n, BigInteger.ONE));
  }

  public BigFraction multiply (long n) {
    return multiply (new BigInteger (Long.toString (n)));
  }

  //-------
  // Divide
  //-------

  public BigFraction divide (BigFraction b) {
    BigInteger an = numerator ();
    BigInteger ad = denominator ();
    BigInteger bn = b.numerator ();
    BigInteger bd = b.denominator ();
    return new BigFraction (an.multiply (bd), ad.multiply (bn));
  }

  public BigFraction divide (BigInteger n) {
    return divide (new BigFraction (n, BigInteger.ONE));
  }

  public BigFraction divide (long n) {
    return divide (new BigInteger (Long.toString (n)));
  }

  //---------------------------------
  // Required to implement Comparable
  //---------------------------------

  public int compareTo (Object other) {
    BigFraction b = (BigFraction) (other);
    BigInteger an = numerator ();
    BigInteger ad = denominator ();
    BigInteger bn = b.numerator ();
    BigInteger bd = b.denominator ();
    BigInteger left = an.multiply (bd);
    BigInteger right = bn.multiply (ad);
    if (lt (left, right)) {
      return -1;
    }
    if (left.equals (right)) {
      return 0;
    }
    else {
      return 1;
    }
  }

  public int compareTo (BigInteger n) {
    Object obj = new BigFraction (n, BigInteger.ONE);
    return compareTo (obj);
  }

  //----------------
  // Override equals
  //----------------

  public boolean equals (Object other) {
    return compareTo ((BigFraction) other) == 0;
  }

  public boolean equals (BigInteger n) {
    return compareTo (n) == 0;
  }

  public boolean equals (long n) {
    return equals (new BigInteger (Long.toString (n)));
  }

  //------------------
  // Override hashCode
  //------------------

  public int hashCode() {
    int num = numerator().intValue ();
    int den = denominator ().intValue ();
    return num ^ den;
  }

  //-----
  // Test
  //----- 

  public static void main (String[] args) {
    BigFraction f1, f2, f3;

    // Start out with a big fraction whose numerator is 3 followed by 500 zeros
    // and whose denominator is 6 followed by 500 zeros

    StringBuffer sb = new StringBuffer ("3");
    for (int i = 0; i < 500; i++) {
      sb.append ("0");
    }
    BigInteger bi1 = new BigInteger (sb.toString ());
    sb.setCharAt (0, '6');
    BigInteger bi2 = new BigInteger (sb.toString ());
    f1 = new BigFraction (bi1, bi2);
    System.out.println (f1);

    // Add 2/8 to it

    f2 = new BigFraction (2, 8);
    System.out.println (f2);
    f3 = f1.add (f2);
    System.out.println (f3);

    // Express result as decimal

    System.out.println (f3.asBigDecimal (2, BigDecimal.ROUND_UNNECESSARY));

    // Subtract 16/64 from result

    System.out.println (f3.subtract (new BigFraction ("16/64")));

    // Divide result by result * negative inverse of result (i.e. -1)

    System.out.println (f3.divide (f3.multiply (f3.inverse ().negate ())));

  }
}

Harmonic Numbers

The harmonic series is the series of reciprocals of positive integers, i.e. 1/1 + 1/2 + 1/3 + 1/4 .... A harmonic number is a partial sum of that series. The first harmonic number is 1/1; the second is 1/1 + 1/2, or 3/2; the third is 1/1 + 1/2 + 1/3, or 11/6; etc.

Here is a table of the first 100 harmonic numbers.

NNumeratorDenominator
111
232
3116
42512
513760
64920
7363140
8761280
971292520
1073812520
118371127720
128602127720
131145993360360
141171733360360
151195757360360
162436559720720
174214222312252240
18142743014084080
1927529579977597520
205583513515519504
21188580535173168
22190931975173168
23444316699118982864
241347822955356948592
25340525224678923714800
26343957422678923714800
2731253625200380313433200
2831540458890380313433200
2992270465113872329089562800
3093046828301472329089562800
3129077425729735772201776446800
32586061125622639144403552893600
335367609007834913127595717600
345406219583474913127595717600
355443726999810913127595717600
365480192543470913127595717600
372040798836801833485721041551200
382053580969474233485721041551200
392066035355155033485721041551200
402078178381193813485721041551200
418569103467049753319914562703599200
42123093129893350192844937529085600
43532145396070491417122332313750680800
4458841824352130757871345655451257488800
4559140858896854644271345655451257488800
4659433392690606272271345655451257488800
4728068260109710696846963245806209101973600
4828200022205979659291963245806209101973600
49138812566871391350266313099044504245996706400
50139432375772240549607593099044504245996706400
51140040031557386823471593099044504245996706400
52140636001654357207453593099044504245996706400
53748469853272339196210427164249358725037825439200
5425050383602118120012840954749786241679275146400
5525149928668012082331288954749786241679275146400
5625247696143443652465478954749786241679275146400
5725343748400008002070998954749786241679275146400
5825438144583183311166078954749786241679275146400
59150632550903198328631329513230237388259077233637600
60151170923801241508170269113230237388259077233637600
61925372872575832277072279171197044480683803711251893600
62928551009361054917576341971197044480683803711251893600
6331055956651021303448974305765681493561267903750631200
64623171679694215690971693339131362987122535807501262400
65625192648726870088010174299131362987122535807501262400
6620906099900553515967764023343787662374178602500420800
67140508745957450343009023164112933773379069966367528193600
68140940183219078279239542016112933773379069966367528193600
69424096103300308736139290480338801320137209899102584580800
70425353434748481578868231134738801320137209899102584580800
713028810706851429109067025637383624893729741902836283505236800
7291124693592935332787128896303491874681189225708508850515710400
73667084944417653637854891458725877136851726813476721146087646859200
74668934292077295215167676426926677136851726813476721146087646859200
75670758981768141571449624262218133136851726813476721146087646859200
76672559662384108370412072783887333136851726813476721146087646859200
776130335977613910418285205667790312441066073952429195098876987200
786146286062324105840330204228030312441066073952429195098876987200
794868007055309996043055960217131137982844219842241906412811281988800
804880292608058024066886120358155997982844219842241906412811281988800
81440318383858380212582431733658471738845597978580177157715301537899200
82441397115319182673211421404577727738845597978580177157715301537899200
833672441655127796364812512959533039359734184632222154704090370027645633600
843681181948368536301765969745576439759734184632222154704090370027645633600
853689819414629973415931738804725211919734184632222154704090370027645633600
863698356445237207772956045432953649519734184632222154704090370027645633600
873706795349055853229324900260857622319734184632222154704090370027645633600
88408665219186421548605851991228895497098076030954443701744994070304101969600
893645196481713595484337076792241271893701718766754945489455304472257065075294400
903653182778990767589396015372875328285861718766754945489455304472257065075294400
913661081314759399341652108474601318124261718766754945489455304472257065075294400
923668893996878372053122809260004199377461718766754945489455304472257065075294400
933676622671662732154792749821908124918261718766754945489455304472257065075294400
943684269126502577787295988888472646995861718766754945489455304472257065075294400
953691835092344109255246562280652279367381718766754945489455304472257065075294400
963699322246041458103739317199996707235031718766754945489455304472257065075294400
9735955302462096692551801824065674567709240769720375229712477164533808935312303556800
9836026445702127011406051348360506519039400769720375229712477164533808935312303556800
9936096870323571165423389261298825016315720769720375229712477164533808935312303556800
100144666362795203511602215180431041314477112788815009188499086581352357412492142272

Some columns of this table correspond to sequences in Neil Sloane's On-Line Encyclopedia of Integer Sequences:

Using the BigInteger.java class, it is an easy matter to write a harmonic number generator (HarmonicNumbers.java). In fact, the table just shown was generated automatically with the aid of this class.

import java.util.Vector;
import java.math.BigInteger;

public class HarmonicNumbers {

  private BigFraction term = null;
  private BigFraction sum = null;

  public BigFraction getNext () {
    if (term == null) {
      term = new BigFraction (1, 1);
      sum = new BigFraction (0, 1);
    }
    else {
      BigInteger num = BigInteger.ONE;
      BigInteger denom = term.denominator ().add (BigInteger.ONE);
      term = new BigFraction (num, denom);
    }
    sum = sum.add (term);
    return sum;
  }

  public static void main (String[] args) {
    HarmonicNumbers gen = new HarmonicNumbers ();
    BigFraction next;
    for (int i = 0; i < 20; i++) {
      next = gen.getNext ();
      System.out.println (next.toString ());
    }
  }

}

Here are some useful links about the harmonic series and harmonic numbers. The best introduction for amateurs is the one by John Webb, which explains the relationship between the harmonic series and such phenomena as rainfall records and traffic flow.

Here are also some references from books:


Wolstenholme's Theorem

In The Book of Prime Number Records (NY: Springer-Verlag, 1988), p. 21, Paulo Ribenboim states that "In 1862, Wolstenholme proved the following interesting result: If p is a prime, p >= 5, then the numerator of 1 + 1/2 + 1/3 + ... + 1/(p-1) is divisible by p^2."

The character Augustus Carmichael in Virginia Woolf's novel To the Lighthouse is based on Joseph Wolstenholme (1829-1891), according to a short biography in the MacTutor History of Mathematics archive.

Note that 1 + 1/2 + 1/3 + ... + 1/(p-1) in Wolstenholme's Theorem is a harmonic number. The following table shows how prime numbers >= 5 are related to harmonic numbers by Wolstenholme's Theorem.

PrimePrime - 1 Harmonic Number
Numerator
Harmonic Number
Denominator
Prime^2Harmonic Number
Numerator / Prime^2
542512251
764920491
11107381252012161
13128602127720169509
171624365597207202898431
191814274301408408036139541
232219093197517316852936093
292831540458890380313433200841375035183
3130930468283014723290895628009619682292227
37365480192543470913127595717600136940030624861
4140207817838119381348572104155120016811236275063173
434212309312989335019284493752908560018496657281227331
47465943339269060627227134565545125748880022092690511212793403

Some columns of this table correspond to sequences in Neil Sloane's On-Line Encyclopedia of Integer Sequences:

Here is a class, WolstenholmeTheorem.java, which applies the primality test inherent in Wolstenholme's Theorem to successive harmonic numbers. It re-uses the Harmonic Number Generator class presented above.

import java.math.BigInteger;

public abstract class WolstenholmeTheorem {

  public static boolean isPrime (BigInteger toCheck, BigFraction harmonicNum) {
    BigInteger squared = toCheck.multiply (toCheck);
    BigInteger mod = harmonicNum.numerator ().mod (squared);
    return mod.equals (BigInteger.ZERO);
  }

  public static void main (String[] args) {
    BigFraction harmonicNumber;
    BigInteger intObj;
    HarmonicNumbers gen = new HarmonicNumbers ();
    int toCheck;
    for (int i = 1; i < 20; i++) {
      harmonicNumber = gen.getNext ();
      toCheck = i + 1;
      if (toCheck >= 5) {
        intObj = new BigInteger (Integer.toString (toCheck));
        System.out.println ("Testing " + toCheck + ", harmonic number " +
                            harmonicNumber.toString ());
        if (WolstenholmeTheorem.isPrime (intObj, harmonicNumber)) {
          System.out.println (toCheck + " is prime");
        }
      }
    }
   }

}

Fractran Algorithm

The mathematician John Horton Conway invented an algorithm known as Fractran:

  1. Start with a list of fractions (f) and an integer (n).
  2. Find the first fraction in f where the product of f(i) and n is an integer. If there is none, stop. If there is, set n equal to the product and repeat step 2.
In some cases, this algorithm will never stop, so it's best to add some termination condition (e.g. repeat step 2 only 100 times).

Here are some useful links about the Fractran algorithm.

We'll use the Fractran algorithm to generate prime numbers, but first here's a general-purpose Fractran.java class:
import java.math.BigInteger;

//-------------------
// Fractran algorithm
//-------------------

public class Fractran {

  private BigFraction[] fractions; 
  private BigInteger seed;
  private BigInteger current;

  //------------
  // Constructor
  //------------

  public Fractran (BigFraction[] fractions, BigInteger seed) {
    this.fractions = fractions;
    this.seed = seed;
    current = null;
  }

  //----------------------------------------
  // Get log base 2 (-1 if not a power of 2)
  //----------------------------------------

  public static int getLogBase2 (BigInteger n) {
    int result = -1;
    if (n.bitCount () == 1) {
      result = n.getLowestSetBit ();
    }
    return result;
  }

  //-----------------------------
  // Get next term (0 if no more)
  //-----------------------------

  public BigInteger getNext () {
    if (current == null) {
      current = seed;
      return current;
    }

    BigFraction fraction = null;
    BigFraction product = null;
    BigInteger result = BigInteger.ZERO;
    for (int i = 0; i < fractions.length; i++) {
      fraction = fractions[i];
      product = fraction.multiply (current);
      if (product.denominator ().equals (BigInteger.ONE)) {
        result = product.numerator ();
        current = result;
        break;
      }
    }
    return result;
  }

}

Conway's Prime Game

The Fractran algorithm is an interesting (but terribly inefficient) way to generate prime numbers. John H. Conway and Richard K. Guy, in The Book of Numbers (NY: Copernicus, 1996), pp. 147-148, give "Fourteen Fruitful Fractions" which yield prime numbers when the Fractran algorithm is applied to them. Here is a Java implementation (PrimeGame.java):

import java.math.BigInteger;

public class PrimeGame {

  public static void main (String[] args) {
    BigFraction[] fractions = {
      new BigFraction ("17/91"),
      new BigFraction ("78/85"),
      new BigFraction ("19/51"),
      new BigFraction ("23/38"),
      new BigFraction ("29/33"),
      new BigFraction ("77/29"),
      new BigFraction ("95/23"),
      new BigFraction ("77/19"),
      new BigFraction ("1/17"),
      new BigFraction ("11/13"),
      new BigFraction ("13/11"),
      new BigFraction ("15/2"),
      new BigFraction ("1/7"),
      new BigFraction ("55/1")
    };

    Fractran app = new Fractran (fractions, new BigInteger ("2"));
    BigInteger next = null;
    int logBase2 = 0;
    for (int i = 0; i < 10000; i++) {
      next = app.getNext ();
      if (next.equals (BigInteger.ZERO)) {
        System.out.println ("Terminated");
        break;
      }
      else {
        logBase2 =  Fractran.getLogBase2 (next);
        if (logBase2 >= 2) {
          System.out.println (logBase2 + " (at iteration " + i + ")");
        }
      }
    }
    System.out.println ("Done");
  }
}

Output from the program just shown is

2 (at iteration 19)
3 (at iteration 69)
5 (at iteration 281)
7 (at iteration 710)
11 (at iteration 2375)
13 (at iteration 3893)
17 (at iteration 8102)
Done
In other words, after ten thousand iterations, it has only generated the first seven prime numbers! The sequence of steps at which it finds primes (19, 69, 281, 710, 2375, 3893, 8102, etc.) is sequence A007547 in Neil Sloane's On-Line Encyclopedia of Integer Sequences.