This post presents C# solutions to a coin change problem as described in http://programmingpraxis.com/2013/06/04/egyptian-fractions.
An Egyptian fraction was written as a sum of unit fractions, meaning the numerator is always 1; further, no two denominators can be the same. As easy way to create an Egyptian fraction is to repeatedly take the largest unit fraction that will fit, subtract to find what remains, and repeat until the remainder is a unit fraction. For instance, 7 divided by 15 is less than 1/2 but more than 1/3, so the first unit fraction is 1/3 and the first remainder is 2/15. Then 2/15 is less than 1/7 but more than 1/8, so the second unit fraction is 1/8 and the second remainder is 1/120. That’s in unit form, so we are finished: 7 ÷ 15 = 1/3 + 1/8 + 1/120. There are other algorithms for finding Egyptian fractions, but there is no algorithm that guarantees a maximum number of terms or a minimum largest denominator; for instance, the greedy algorithm leads to 5 ÷ 121 = 1/25 + 1/757 + 1/763309 + 1/873960180913 + 1/1527612795642093418846225, but a simpler rendering of the same number is 1/33 + 1/121 + 1/363.
…
Your task is to write a program that calculates the ratio of two numbers as an Egyptian fraction…
As presented in the original post, you can use a greedy algorithm. Thats not fun! Let’s try and improve it: i.e. to return the smallest amount of unit fractions. You cannot devise an algorithm to guarantee the smallest amount of terms, since the problem space has infinite possibilities (genetic algorithm? food for thought).
So what approach can we take to improve on the greedy solution? I began by writing out a few iterations of calculations for 5 ÷ 121: from 1/25 up to 1/33 (part of an optimal solution presented in the problem definition). I noticed that when choosing 1/33, the remaining fraction (that we are pulling apart) can be simplified: where as all other fractions leading up to 1/33 leaves a fraction that cannot be further simplified! If you think about it, simplifying keeps the size of the denominator down, keeping smaller denominators helps yields a smaller amount of terms. This is because when we are dealing with very small numbers (large denominators), we are getting to the final target at a slower rate that we would with larger numbers. Simple huh?
So how can you simplify a fraction? It can be done by calculating the gcd (greatest common divisor) between the numerator and the denominator, then dividing the numerator and the denominator by the gcd. If the gcd is 1, then the fraction cannot be simplified. Hmmm… so maybe we can decide on the next unit fraction (for subtracting) only if the result can be simplified. Using this informal idea as the basis of our algorithm, we get the following solution:
public class EgyptionFractions { public static List<int[]> GetFractions(int numerator, int denominator) { if (numerator >= denominator) throw new ArgumentOutOfRangeException ("denominator"); if (numerator <= 0) throw new ArgumentOutOfRangeException ("numerator"); var fractions = new List<int[]> (); int subDenominator = 2; do { // First find the next fraction to substract from that is small enough int leftNumerator = numerator * subDenominator; while (leftNumerator < denominator) { // Note: rightNumerator == denominator subDenominator++; leftNumerator += numerator; } // Now we have a valid unit fraction to substract with, lets continue // searching for the next unit fraction that yeilds a remainder that // can be simplified (to keep the denominators small). while (true) { int remainingNumerator = leftNumerator - denominator; if(remainingNumerator == 0) { // The fractions are the same numerator = 0; fractions.Add (new [] {1, subDenominator}); break; } int remainingDenominator = denominator * subDenominator; int gcd = GCD (remainingNumerator, remainingDenominator); if (gcd > 1 || remainingNumerator == 1) { // The resultant fraction can be simplified using this denominator numerator = remainingNumerator / gcd; denominator = remainingDenominator / gcd; fractions.Add (new [] {1, subDenominator}); // Finished? if(numerator == 1) fractions.Add (new [] {1, denominator}); break; } subDenominator++; leftNumerator += numerator; // i.e. additive version of subDenominator * numerator; } subDenominator++; } while (numerator > 1); return fractions; } private static int GCD(int n1, int n2) { if (n2 == 0) return n1; return GCD (n2, n1 % n2); } }
If you pass in 5, 121, the result will be:
1/33, 1/91, 1/33033
Filed under: Programming Problems - Resolved
