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.
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:
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.
Suppose you were asked for a list of all fractions between 0 and 1 inclusive, whose denominator does not exceed a given number n.
[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:
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.
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.
System.out.println (Long.MAX_VALUE); System.out.println (Long.MAX_VALUE + 1); System.out.println (Long.MIN_VALUE);The output is:
9223372036854775807 -9223372036854775808 -9223372036854775808I 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 ())));
}
}
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.
| N | Numerator | Denominator |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3 | 2 |
| 3 | 11 | 6 |
| 4 | 25 | 12 |
| 5 | 137 | 60 |
| 6 | 49 | 20 |
| 7 | 363 | 140 |
| 8 | 761 | 280 |
| 9 | 7129 | 2520 |
| 10 | 7381 | 2520 |
| 11 | 83711 | 27720 |
| 12 | 86021 | 27720 |
| 13 | 1145993 | 360360 |
| 14 | 1171733 | 360360 |
| 15 | 1195757 | 360360 |
| 16 | 2436559 | 720720 |
| 17 | 42142223 | 12252240 |
| 18 | 14274301 | 4084080 |
| 19 | 275295799 | 77597520 |
| 20 | 55835135 | 15519504 |
| 21 | 18858053 | 5173168 |
| 22 | 19093197 | 5173168 |
| 23 | 444316699 | 118982864 |
| 24 | 1347822955 | 356948592 |
| 25 | 34052522467 | 8923714800 |
| 26 | 34395742267 | 8923714800 |
| 27 | 312536252003 | 80313433200 |
| 28 | 315404588903 | 80313433200 |
| 29 | 9227046511387 | 2329089562800 |
| 30 | 9304682830147 | 2329089562800 |
| 31 | 290774257297357 | 72201776446800 |
| 32 | 586061125622639 | 144403552893600 |
| 33 | 53676090078349 | 13127595717600 |
| 34 | 54062195834749 | 13127595717600 |
| 35 | 54437269998109 | 13127595717600 |
| 36 | 54801925434709 | 13127595717600 |
| 37 | 2040798836801833 | 485721041551200 |
| 38 | 2053580969474233 | 485721041551200 |
| 39 | 2066035355155033 | 485721041551200 |
| 40 | 2078178381193813 | 485721041551200 |
| 41 | 85691034670497533 | 19914562703599200 |
| 42 | 12309312989335019 | 2844937529085600 |
| 43 | 532145396070491417 | 122332313750680800 |
| 44 | 5884182435213075787 | 1345655451257488800 |
| 45 | 5914085889685464427 | 1345655451257488800 |
| 46 | 5943339269060627227 | 1345655451257488800 |
| 47 | 280682601097106968469 | 63245806209101973600 |
| 48 | 282000222059796592919 | 63245806209101973600 |
| 49 | 13881256687139135026631 | 3099044504245996706400 |
| 50 | 13943237577224054960759 | 3099044504245996706400 |
| 51 | 14004003155738682347159 | 3099044504245996706400 |
| 52 | 14063600165435720745359 | 3099044504245996706400 |
| 53 | 748469853272339196210427 | 164249358725037825439200 |
| 54 | 250503836021181200128409 | 54749786241679275146400 |
| 55 | 251499286680120823312889 | 54749786241679275146400 |
| 56 | 252476961434436524654789 | 54749786241679275146400 |
| 57 | 253437484000080020709989 | 54749786241679275146400 |
| 58 | 254381445831833111660789 | 54749786241679275146400 |
| 59 | 15063255090319832863132951 | 3230237388259077233637600 |
| 60 | 15117092380124150817026911 | 3230237388259077233637600 |
| 61 | 925372872575832277072279171 | 197044480683803711251893600 |
| 62 | 928551009361054917576341971 | 197044480683803711251893600 |
| 63 | 310559566510213034489743057 | 65681493561267903750631200 |
| 64 | 623171679694215690971693339 | 131362987122535807501262400 |
| 65 | 625192648726870088010174299 | 131362987122535807501262400 |
| 66 | 209060999005535159677640233 | 43787662374178602500420800 |
| 67 | 14050874595745034300902316411 | 2933773379069966367528193600 |
| 68 | 14094018321907827923954201611 | 2933773379069966367528193600 |
| 69 | 42409610330030873613929048033 | 8801320137209899102584580800 |
| 70 | 42535343474848157886823113473 | 8801320137209899102584580800 |
| 71 | 3028810706851429109067025637383 | 624893729741902836283505236800 |
| 72 | 9112469359293533278712889630349 | 1874681189225708508850515710400 |
| 73 | 667084944417653637854891458725877 | 136851726813476721146087646859200 |
| 74 | 668934292077295215167676426926677 | 136851726813476721146087646859200 |
| 75 | 670758981768141571449624262218133 | 136851726813476721146087646859200 |
| 76 | 672559662384108370412072783887333 | 136851726813476721146087646859200 |
| 77 | 61303359776139104182852056677903 | 12441066073952429195098876987200 |
| 78 | 61462860623241058403302042280303 | 12441066073952429195098876987200 |
| 79 | 4868007055309996043055960217131137 | 982844219842241906412811281988800 |
| 80 | 4880292608058024066886120358155997 | 982844219842241906412811281988800 |
| 81 | 44031838385838021258243173365847173 | 8845597978580177157715301537899200 |
| 82 | 44139711531918267321142140457772773 | 8845597978580177157715301537899200 |
| 83 | 3672441655127796364812512959533039359 | 734184632222154704090370027645633600 |
| 84 | 3681181948368536301765969745576439759 | 734184632222154704090370027645633600 |
| 85 | 3689819414629973415931738804725211919 | 734184632222154704090370027645633600 |
| 86 | 3698356445237207772956045432953649519 | 734184632222154704090370027645633600 |
| 87 | 3706795349055853229324900260857622319 | 734184632222154704090370027645633600 |
| 88 | 40866521918642154860585199122889549709 | 8076030954443701744994070304101969600 |
| 89 | 3645196481713595484337076792241271893701 | 718766754945489455304472257065075294400 |
| 90 | 3653182778990767589396015372875328285861 | 718766754945489455304472257065075294400 |
| 91 | 3661081314759399341652108474601318124261 | 718766754945489455304472257065075294400 |
| 92 | 3668893996878372053122809260004199377461 | 718766754945489455304472257065075294400 |
| 93 | 3676622671662732154792749821908124918261 | 718766754945489455304472257065075294400 |
| 94 | 3684269126502577787295988888472646995861 | 718766754945489455304472257065075294400 |
| 95 | 3691835092344109255246562280652279367381 | 718766754945489455304472257065075294400 |
| 96 | 3699322246041458103739317199996707235031 | 718766754945489455304472257065075294400 |
| 97 | 359553024620966925518018240656745677092407 | 69720375229712477164533808935312303556800 |
| 98 | 360264457021270114060513483605065190394007 | 69720375229712477164533808935312303556800 |
| 99 | 360968703235711654233892612988250163157207 | 69720375229712477164533808935312303556800 |
| 100 | 14466636279520351160221518043104131447711 | 2788815009188499086581352357412492142272 |
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.
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.
| Prime | Prime - 1 | Harmonic Number Numerator |
Harmonic Number Denominator |
Prime^2 | Harmonic Number Numerator / Prime^2 |
|---|---|---|---|---|---|
| 5 | 4 | 25 | 12 | 25 | 1 |
| 7 | 6 | 49 | 20 | 49 | 1 |
| 11 | 10 | 7381 | 2520 | 121 | 61 |
| 13 | 12 | 86021 | 27720 | 169 | 509 |
| 17 | 16 | 2436559 | 720720 | 289 | 8431 |
| 19 | 18 | 14274301 | 4084080 | 361 | 39541 |
| 23 | 22 | 19093197 | 5173168 | 529 | 36093 |
| 29 | 28 | 315404588903 | 80313433200 | 841 | 375035183 |
| 31 | 30 | 9304682830147 | 2329089562800 | 961 | 9682292227 |
| 37 | 36 | 54801925434709 | 13127595717600 | 1369 | 40030624861 |
| 41 | 40 | 2078178381193813 | 485721041551200 | 1681 | 1236275063173 |
| 43 | 42 | 12309312989335019 | 2844937529085600 | 1849 | 6657281227331 |
| 47 | 46 | 5943339269060627227 | 1345655451257488800 | 2209 | 2690511212793403 |
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");
}
}
}
}
}
The mathematician John Horton Conway invented an algorithm known as Fractran:
Here are some useful links about the Fractran algorithm.
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;
}
}
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) DoneIn 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.